DevSecOps Lab
===
## Task 1: Environment Setup
### 1. Creation of two VMs, for the Jenkins server and its agent.
#### a. Installation of Docker Engine on both VMs
I deployed docker on the agent and server using my Ansible playbook [available here](https://raw.githubusercontent.com/octopus237/infrastructure-as-code/main/deploy-docker/playbook.yaml)
Before running the playbook i made a ping-pong test
||
| -------- |
| 
Fig.1. Connectivity test|
Jenkins-agent = 10.1.1.40
Jenkins-server = 10.1.1.11
Since some of the configurations in my play where already satisfied on both hosts, i started the play at the installation phase
| |
| -------- |
| 
Fig.2. running the playbook |
After installation docker is present on both hosts as shown on screenshots below:
| |
| --- |
| 
Fig.3. Jenkins server |
| |
| -------- |
|
Fig.4. Jenkins agent|
I deployed jenkins server using the jenkins docker instance [Reference](https://www.jenkins.io/doc/book/installing/docker/)
| |
| -------- |
| 
Fig.5. jenkins docker running |
After deploying the server i added the agent as shown on the screenshot below:
| |
| -------- |
|
Fig.6. adding agent|
For the agent to node to successfully connect with the master, i generated an ssh-key pair, i added the public-key to the agent's `.ssh/Authorized_keys` file and the private key as a credential in Jenkins sever
| |
| -------- |
| 
Fig.7. Agent added |
### 3. Prepare the application
This lab has been realized with an open source application available at https://github.com/HamidullahMuslih/ot-lab-sdlc.git
| |
| -------- |
| 
Fig.8. Cloning the project |
I cloned the repository and later i pushed it on my github account with the Dockerfile for the project
https://github.com/octopus237
The application is a Maven project, after making some few research about Maven applications, i can say that;
- Its life cycle is made of 3 phases, the **build & deployment**, the **cleaning** and the **site documentation** named respectively as **default**, **clean** and **site**
**Some important maven Commands:**
- build: `mvn package`
- unit test: `mvn test`
- integration test: `mvn failsafe:integration-test`
- packaging: `mvn package`
- cleaning: `mvn clean`
- Deploying: `mvn deploy`
- validate: `mvn validate`
- site: `mvn site:site`
## Task 2: Implement DevSecOps Pipeline
Before starting the SDLC, i prepared the jenkins infrastructure by installing necessary plugins such as `docker`, `git` and i also added `Maven` .
| |
| -------- |
| 
Fig.9. installing maven|
### 1. Creation of the first job to fetch and build the Maven project.
- The job below cleans the workspace (deletes existing artifacts), pulls the main branch of the provided git repository and runs the `mvn package` command (the build command)
```!jenkins=
pipeline {
agent {
label 'agent1'
}
tools {
maven "Maven3"
}
stages {
stage('Cleanup Workspace'){
steps {
script {
cleanWs()
}
}
}
stage('Fetch') {
steps {
git branch: 'main', url: 'https://github.com/octopus237/devsecops-project.git'
}
}
stage('Build') {
steps {
sh "mvn package"
}
}
}
}
```
| |
| -------- |
| 
Fig.10. build job |
| |
| -------- |
| 
Fig.11. Logs |
### 2. Creating the next job to perform the unit tests of the project.
| |
| -------- |
| 
Fig.12. build job workspace |
In order to use the same workspace (artifacts) with the different builds, i copied the workspace from the first job and configured the other jobs to run in the same workspace.
- The job below makes a unit test on the code pulled during the `build` job
```!jenkins=
pipeline {
agent {
label 'agent1'
}
tools {
maven "Maven3"
}
stages {
stage('Unit test') {
steps {
dir('/home/brice/jenkins-agent/workspace/Devsecops-pipeline/fetch-build'){
sh "mvn test"
}
}
}
}
}
```
||
| -------- |
|
Fig.13. unit-test job |
| |
| -------- |
| 
Fig.14. Logs |
### 3. Creating the next job to perform the integration test on the project.
- The job below makes an integration test with the artifacts generated during the `build` job
```!jenkins=
pipeline {
agent {
label 'agent1'
}
tools {
maven "Maven3"
}
stages {
stage('Integration test') {
steps {
dir('/home/brice/jenkins-agent/workspace/Devsecops-pipeline/fetch-build'){
sh "mvn failsafe:integration-test"
}
}
}
}
}
```
| |
| -------- |
| 
Fig.15. Integration test |
### 4. Performing static code analysis on the project
I added the Warning NG plugin as shown below
| |
| -------- |
|
Fig.16. Warning NG plugin|
- The job below uses the maven plugins `checkstyle`, `pmd` and `spotbugs` for code analysis and generates a report in UTF-8 encoding
```!jenkins=
pipeline {
agent {
label 'agent1'
}
tools {
maven "Maven3"
}
stages {
stage('testing-step') {
steps {
dir('/home/brice/jenkins-agent/workspace/Devsecops-pipeline/fetch-build'){
sh'''
mvn checkstyle:checkstyle
mvn pmd:pmd
mvn com.github.spotbugs:spotbugs-maven-plugin:4.7.3.4:spotbugs
'''
recordIssues(tools: [checkStyle(reportEncoding: 'UTF-8'), pmdParser(reportEncoding: 'UTF-8'), spotBugs(reportEncoding: 'UTF-8', useRankAsPriority: true)])
}
}
}
}
}
```
The figure below shows the results of the analysis
| |
| -------- |
| 
Fig.17. Results |
1. **Checkstyle** helps the development Team to enforce coding standard, the results from Check style are shown in the figure below:
- The figure below shows the different categories in which errors have been found
| |
| -------- |
| 
Fig.18. Checkstyle results |
- Further more looking at the issues menu, we have more description such as the case of the first issue which declares that the `package-info.java` file is missing
||
| -------- |
| 
Fig.19 Checkstyle Issues|
- Opening that particular issue, Checkstyle shows the file in which we should make modifications
| |
| -------- |
| 
Fig.20 Issue 1|
2. **PMD(Programming Mistake Detector)** finds programming flaws as unused variables, empty catch blocks, unnecessary object creation, and so forth.
- The figure below shows the flaws found in our code
| |
| -------- |
|
Fig.21. Type of flaws |
- The figure below shows the modules that have been imported but are not used in the program (`unnecessary import` type)
| |
| -------- |
| 
Fig.22. Unnecessary imports |
- Going deeper in to the first issue we can clearly see the unnecessary module
| |
| -------- |
| 
Fig.23. unused java.util.map |
3. **SpotBugs** is mainly used to find bugs in the Java code, the static code analysis made reports 15 bugs in different categories namely
`Bad-practice`: practices such as bad exception handling
`I18N`: Code flaws having to do with internationalization
`Malicious-code`: Code that is vulnerable to attacks from untrusted code
`Style`: Code that is confusing, anomalous, or written in a way that leads itself to errors
| |
| -------- |
| 
Fig.24. Spotbugs categories |
- I opened an issue from the Malicious_Code category, this shows that the `getUsers()` method might return data.
| |
| -------- |
|
Fig.25. Issue by spotbugs|
#### b. Feed the static code analysis reports to the Violations Plugin and set the quality gates for the number of bugs in one of the reports, validate your work to fail/unstable the job.
- I added the Violation module as shown on the screenshot below
| |
| -------- |
| 
Fig.26. Violation plugin|
- I configured the module by setting the values to trigger the `Fail/Unstable` state at 15 warnings
| |
| -------- |
| 
Fig.27 configuring Quality-gate |
After running the job, i had the results shown below:
- Fig.31 Shows that PMD succeeded since it has less than 15 warnings while Checkstyle and SpotBugs failed since they have more than or equal to 15 warnings
| |
| -------- |
| 
Fig.28. Results |
- Fig.32. Shows that the job completed successfully but due to the Quality Gate the result turned to Fail
| |
| -------- |
| 
Fig.29. Logs of QG |
### 5. Dockerizing artifacts and pushing to docker hub
My docker file is as shown below, this is file is present on my github acccount and in my jenkins workspace since it has been pulled during the first step of the pipeline
```Dockerfile!=
FROM tomcat:8.5.47-jdk8-openjdk
WORKDIR /usr/local/tomcat/webapps
COPY target/vprofile-v2.war .
EXPOSE 8080
```
To be able to push images to my docker hub account, i generated a token on docker hub and added it as credentials in jenkins
| |
| -------- |
|
Fig.30. dockerhub credentials |
- The job below builds the docker image, then pushes it to docker hub with the latest tag and build_number tag later on deletes the built images (optional: this is just to save memory) from the docker agent.
```!jenkinsfile=
pipeline {
agent {
label 'agent1'
}
environment{
IMAGE_TAG = "${BUILD_NUMBER}"
IMAGE_NAME = "jobri237" + "/" + "ot-lab"
REGISTRY_CREDS = 'docker-hub'
}
stages {
stage('Build image') {
steps {
dir('/home/brice/jenkins-agent/workspace/Devsecops-pipeline/fetch-build'){
script{
docker_image = docker.build "${IMAGE_NAME}"
}
}
}
}
stage('Push image') {
steps {
script{
docker.withRegistry('', REGISTRY_CREDS) {
docker_image.push("${IMAGE_TAG}")
docker_image.push('latest')
}
}
}
}
stage('Delete local image') {
steps {
sh "docker rmi ${IMAGE_NAME}:${IMAGE_TAG}"
sh "docker rmi ${IMAGE_NAME}:latest"
}
}
}
}
```
| |
| -------- |
| 
Fig.31. docker build-push|
| |
| -------- |
| 
Fig.32. docker hub |
### 6. Create the next job to deploy the docker image from the docker hub to the Jenkins agent and validate/show that you can access the web app.
- The job below pulls the latest image from docker hub and runs it exposing the port 80
```!jenkinsfile=
pipeline {
agent { label 'agent1'
}
environment{
IMAGE_NAME = "jobri237" + "/" + "ot-lab" + ":latest"
}
stages {
stage('deployment'){
steps {
sh '''
docker pull ${IMAGE_NAME}
docker stop visual-path
docker rm visual-path
docker run --name visual-path --restart on-failure -d -p 80:8080 ${IMAGE_NAME}
'''
}
}
}
}
```
As shown below the application is accessible on the agent on port 80
| |
| -------- |
| 
Fig.33. Application running |
### 7. Finally, chain all the jobs such that when you run the 1st job it should execute the rest. Note: Use Post-Build Actions>Build other projects.
All the jobs where created in the `OT_LAB03_DevSecOps` Folder for simplicity of management. The figure below shows my folder with the different jobs and their order
| |
| -------- |
| 
Fig.34. Jobs |
- In order to chain the pipelines, i proceeded as shown on the screen shots below






After adding all this parameters i was able to trigger all the jobs just by building the first job.
| |
| -------- |
| 
F.g.35 Build history |