Implement a DevSecOps Pipeline with Jenkins for the Boardgame app.
Pre-requisites
Generate terraform-key required for connection to EC2 instances in AWS VPC.
Choose RSA key pair type and use .pem key file format.
We'll implement the following workflow:
-
Provision Servers with Terraform
-
Ansible Setup and User Configuration
-
Setup Kubernetes Cluster with Kubeadm
-
Setup MetalLB
-
Setup Traefik Proxy
-
Setup Cert-manager
-
Jenkins Installation and Configuration
-
Install Jenkins Plugins
-
SonarQube Installation and Setup
-
Sonatype Nexus Installation and Setup
-
Setup Trivy
-
DockerHub Setup
-
Setup Mail Notifications
-
Pipeline Setup with Jenkinsfile
-
Setup Prometheus and Grafana for Monitoring and Observability
Please check the inventory list for reference to IP addresses of EC2 instances used in this project.
Install AWS CLI in local machine
sudo apt install curl unzip
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install -i /usr/local/aws-cli -b /usr/local/binConfirm the AWS CLI installation
aws --versionClone this repository in the local machine
cd /
git clone git@github.com:odennav/boardgame-devops-pipeline-project.gitExecute these Terraform commands sequentially in the local machine to create the Virtual Private Cloud and EC2 instances.
Initializes terraform working directory
cd boardgame-devops-pipeline-project/terraform
terraform initValidate the syntax of the terraform configuration files
terraform validateCreate an execution plan that describes the changes terraform will make to the infrastructure
terraform planApply the changes described in execution plan
terraform apply -auto-approveCheck AWS console for instances created and running
SSH access
Use .pem key from AWS to SSH into the public EC2 instance. IPv4 address of public EC2 instance will be shown in terraform outputs.
ssh -i private-key/terraform-key.pem ec2-user@<ipaddress>We can use public EC2 instance as a jumpbox to securely SSH into private EC2 instances within the VPC.
Note, the ansible inventory is built dynamically by terraform with the private ip addresses of the EC2 machines.
Generate SSH public/private key pair in build machine
ssh-keygen -t rsa -b 4096Once the RSA key-pair is generated, manually copy the public key id_rsa.pub to the /root/.ssh/authorized_keys file in all kube nodes.
Install Ansible in build machine
sudo apt install -y software-properties-common
sudo add-apt-repository --yes --update ppa:ansible/ansible
sudo apt install -y ansibleThe bootstrap and kubeadm folders in this repository contain the ansible scripts necessary to set up your servers with the required packages and applications.
Bootstrapping EC2 Nodes
All nodes will be bootstrapped using Ansible.
Bootstrap the master node
cd boardgame-devops-pipeline-project/bootstrap/
ansible-playbook bootstrap.yml --limit k8s_masterBootstrap the worker nodes
ansible-playbook bootstrap.yml --limit k8s_nodeOnce the bootstrap is complete, you can log in as odennav-admin.
Confirm SSH access to master node
ssh odennav-admin@10.33.100.2To return to the build machine, type exit and press Enter or use Ctrl+D
Confirm SSH access to 1st worker node
ssh odennav-admin@10.33.100.3Confirm SSH access to 2nd worker node
ssh odennav-admin@10.33.100.4Setting up Kubernetes Cluster
The kube nodes are now ready to have a Kubernetes cluster installed on them.
Please note amazon EKS is preferred for production but it's not deployed in this project to reduce the financial costs incurred in service bill.
Execute ansible role playbook for the kubernetes master node
cd boardgame-devops-pipeline-project/kubeadm/
ansible-playbook k8s.yml --limit k8s_masterExecute ansible role playbook for the kubernetes worker nodes
ansible-playbook k8s.yml --limit k8s_nodeCheck status of your nodes from the k8smaster node
kubectl get nodesThe Kubernetes cluster should be ready as shown below.
Install Kubeaudit
Audit the Kubernetes clusters for various security concerns with Kubeaudit tool.
Download the binary relase
cd $HOME
wget https://github.com/Shopify/kubeaudit/releases/download/v0.22.1/kubeaudit_0.22.1_linux_amd64.tar.gzExtract the tarball file
tar -xzvf kubeaudit_0.22.1_linux_amd64.tar.gzMove kubeaudit to bin directory in $PATH
sudo mv kubeaudit /usr/local/bin/Use cluster mode and audit all Kubernetes resources in the cluster
kubeaudit allSetup RBAC in Kubernetes Cluster
Role Based Access Control is a method of regulating access to computer or network resources based on the roles of individual users.
We'll start by creating the boardgame namespace
kubectl create namespace boardgameView the manifest for the service account
apiVersion: v1
kind: ServiceAccount
metadata:
name: jenkins
namespaces: boardgameCreate a service account for Jenkins
kubectl apply -f boardgame-devops-pipeline-project/kubernetes-manifests/service_account.yamlCreate a role to assign to the service account
kubectl apply -f boardgame-devops-pipeline-project/kubernetes-manifests/role.yamlBind the role to service account
kubectl apply -f boardgame-devops-pipeline-project/kubernetes-manifests/role_binding.yamlCreate API token for Jenkins to athenticate to the kubernetes cluster API
kubectl apply -f boardgame-devops-pipeline-project/kubernetes-manifests/svc_account_token.yamlView API token created in kubernetes secret
kubectl describe secret jenkins_secret -n boardgameThis token will be used to create a global credential for kubernetes tool in Jenkins.
Create Kubernetes Secret Credential in Jenkins
Go to Jenkins Dashboard and select Manage Jenkins.
Under the Security section, select Credentials
Click on (global) domain of jenkins System store.
Next, click on blue button + Add Credentials at the top right.
Assign the following:
Kind -------------------------> Secret text
Scope ------------------------> Global
Secret -----------------------> <jenkins_secret token>
ID ------------------------- > k8s-cred
Description ------------------> k8s-cred-token
MetalLB is a load-balancer implementation for bare metal Kubernetes clusters which will allocate IP addresses to services within the Kubernetes cluster.
Enable strictARP mode to use layer2 ARP protocol
kubectl get configmap kube-proxy -n kube-system -o yaml | \
sed -e "s/strictARP: false/strictARP: true/" | \
kubectl apply -f - -n kube-systemProceed with installation by manifest
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.5/config/manifests/metallb-native.yamlDefine a pool of addresses to assign to services with IPAddressPool resource
cd boardgame-devops-pipeline-project/metallb/
kubectl apply -f IPAddressPool.ymlConfigure L2Advertisement resource to advertise this IP pool to local network.
kubectl apply -f L2Advertisement.ymlTraefik proxy is a modern cloud native application proxy that will automate the discovery, routing, and load balancing of services in the kubernetes cluster.
Create namespace for traefik
kubectl create namespace traefikConfirm traefik namespace created
kubectl get namespacesAdd traefik repository with helm package manager
helm repo add traefik https://helm.traefik.io/traefikUpdate local cache of helm chart repositories
helm repo updateInstall traefik chart with custom values file values.yaml in traefik namespace
cd boardgame-devops-pipeline-project/traefik/
helm install --namespace=traefik traefik traefik/traefik --values=values.yamlVerify Traefik is installed
helm status traefik --namespace=traefikVerify the status of the traefik ingress controller service kubectl get svc --namespace traefik
Confirm the traefik deployment pods in traefik namespace are ready and running
kubectl get pods --namespace traefikSetup Route to Traefik Dashboard
Apply middleware configuration for ingress route to the boardgame service
kubectl apply -f default-headers.yamlConfirm middleware is created
kubectl get middlewareInstall the apache2-utils package to use the htpassword tool
sudo apt-get update
sudo apt-get install -y apache2-utilsGenerate a base64 encoded password
htpasswd -nb odennav-admin mypassword | openssl base64Copy encoded password to the traefik/traefik-dasboard/secret-dashboard.yaml file.
Apply the secret to the kubernetes cluster
cd boardgame-devops-pipeline-project/traefik/traefik-dasboard/dasboard/
kubectl apply -f secret-dashboard.yamlVerify secret created
kubectl get secrets --namespace traefikApply middleware configuration for ingress route to traefik dashboard
kubectl apply -f middleware.yamlApply ingress route to the traefik dashboard
kubectl apply -f ingress.yamlcert-manager is a powerful & extensible X.509 certificate controller used to automatically provision and manage TLS certificates in Kubernetes clusters.
Create namespace for cert-manager
kubectl create namespace cert-managerConfirm cert-manager namespace created
kubectl get namespacesAdd jetstack repository with helm package manager
helm repo add jetstack https://charts.jetstack.ioUpdate local cache of helm chart repositories
helm repo updateApply CRDs for cert-manager
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.15.0/cert-manager.crds.yamlInstall cert-manager chart with custom values file values.yaml in cert-manager namespace
cd boardgame-devops-pipeline-project/traefik/traefik-dasboard/cert-manager/
helm install cert-manager jetstack/cert-manager --namespace cert-manager --values=values.yaml --version v1.15.0Confirm all cert-manager deployment pods in cert-manager namespace are ready and running
kubectl get pods --namespace cert-managerConfigure Staging Certificate
At this moment, we'll use the staging endpoint for certificate propagation.
The api url for the production endpoint is configured in a way that triggers a block for days/weeks if there are too many failures.
The Staging endpoint wont give us a trusted certificate but it will assign one that is signed from staging servers.
If we get staging certificate, thats good. Then we can use the caServer endpoint url to get production certificate.
I've registered a domain odennav.com with a domain registrar.
Update nameservers set with your registrar to ensure this domain points to the authoritave nameservers received from Cloudflare.
Cloudflare will be used for DNS verification by Let's Encrypt.
Go to your cloudflare account and create a custom token. Implement the following:
-
On your profile page, select
{} API Tokens -
Click on
Create Tokenand selectGet Startedto create a custom token.
Enter the following:
Token name --------------------> Odennav-Docker-Traefik
1st Permissions ---------------> Zone & Zone & Read
2nd Permissions ---------------> Zone & DNS & Edit
Zone Resources ----------------> Include & Specific zone & odennav.com
Click on Continue to summary and on the summary page, click on Create Token
Copy the token shown to you for access to the Cloudflare API and save it to the cert-manager/issuer/secret-cf-token.yaml file.
Apply the secrets for cloudflare secret token to the kubernetes cluster
cd boardgame-devops-pipeline-project/traefik/traefik-dasboard/cert-manager/issuers/
kubectl apply -f secret-cf-token.yamlApply the staging ClusterIssuer resource
kubectl apply -f letsencrypt-staging.yamlCreate a staging certificate for the default namespace
cd boardgame-devops-pipeline-project/traefik/traefik-dasboard/cert-manager/certificates/staging
kubectl apply -f local-odennav-com.yamlConfigure DNS for Traefik Proxy
In this step, configure the DNS within your Cloudflare account, using a domain, odennav.com that you own.
Create the domain A records for each Host configured for the ingress route resources:
-
boardgame.odennav.com -
traefik.odennav.com
Identify the load balancer EXTERNAL-IP created by the traefik deployment and use it to add required A records for the hosts above.
kubectl get svc -n traefikView the traefik dashboard on a broswer and note the staging certificate issued.
Configure Production Certificate
Once the staging certificate from let's encrypt is confirmed, we can now go to production.
Apply production ClusterIssuer. This points to Let's Encrypt production endpoint.
cd boardgame-devops-pipeline-project/traefik/traefik-dasboard/cert-manager/issuers/
kubectl apply -f letsencrypt-production.yamlCreate a production certificate resource. This creates a TXT record at cloudflare and verifies it to create our production certificate.
cd boardgame-devops-pipeline-project/traefik/traefik-dasboard/cert-manager/certificates/production
kubectl apply -f local-odennav-com.yamlCheck for DNS solver challenges. If no challenges after pending or valid states are resolved, then its working well.
kubectl get challengesReplace the value of the secretName variable defined in the ingress.yaml file for traefik dashboard. Use local-odennav-com-tls as name of certificate secret.
This secret stores the production certificate in the IngressRoute resource for the traefik dashboard.
Recreate the IngressRoute.
cd cd boardgame-devops-pipeline-project/traefik/traefik-dasboard/dasboard/
kubectl apply -f ingress.yamlInstall Jenkins in Ubuntu
We'll use the long term support release which is installed from debian-stable apt repository.
sudo wget -O /usr/share/keyrings/jenkins-keyring.asc \
https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key
echo "deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc]" \
https://pkg.jenkins.io/debian-stable binary/ | sudo tee \
/etc/apt/sources.list.d/jenkins.list > /dev/null
sudo apt-get update
sudo apt-get install jenkinsEnable jenkins service
sudo systemctl enable jenkinsStart jenkins service
sudo systemctl start jenkinsConfirm jenkins service is active and running
sudo systemctl status jenkinsPost Installation Setup
Next we use the post-installation setup wizard to unlock jenkins, customize plugins and create first admin user required to continue accessing jenkins.
-
Browse to
10.33.100.7:8080to see theUnlock Jenkinspage. -
Obtain the automatically-generated alphanumeric password
sudo cat /var/jenkins_home/secrets/initialAdminPassword
-
Paste this password into the
Administrator passwordfield and clickContinueto access jenkin's main UI.
Customize Jenkins with Plugins
After unlocking Jenkins, the Customize Jenkins page appears.
Here you can install any number of useful plugins as part of your initial setup.
Click on Install suggested plugins to install the recommended set of plugins, which are based on most common use cases.
Create First Administrator User
Finally, after customizing Jenkins with plugins, Jenkins asks you to create your first administrator user.
When the Create First Admin User page appears, specify the details for your administrator user in the respective fields and click Save and Continue.
When the Jenkins is ready page appears, click Start using Jenkins
This page may indicate Jenkins is almost ready! instead and if so, click Restart.
If the page does not automatically refresh after a minute, use your web browser to refresh the page manually.
If required, log in to Jenkins with the credentials of the user you just created and you are ready to start using Jenkins.
Install Kubectl using native package management
The kubectl version will be the same version with the kubernetes cluster.
We'll use this ansible script below to install kubectl in the jenkins machine.
cd boardgame-devops-pipeline-project/jenkins/
ansible-playbook kubectl_install.yml Plugins are required to integrate tools to Jenkins and execute in our pipeline script.
Go to Plugin Manager under Manage Jenkins section of Jenkins dasboard.
Our next task is to search and install the following plugins below:
-
Eclipse Temurin installer
-
Config File Provider
-
Pipeline Maven Integration
-
SonarQube Scanner
-
Maven Integration
-
Docker
-
Docker Pipeline
-
docker-build-step
-
CloudBees Docker Build and Publish
-
Kubernetes
-
Kubernetes CLI
-
Kubernetes Credentials
-
Kubernetes Client API
-
Trivy
Select Install without restart at bottom left.
Configure Other Global Tools
When plugins selected are installed, next we configure them.
Go to Global Tool Configuration under Manage Jenkins section of Jenkins dasboard
Note procedures to configure jdk and docker as global tools below:
Procedure - JDK:
Scroll down and search for JDK installations
Click on Add JDK
Enter or select the following:
-
Name -----------------------------> jdk17
-
Install automatically
?------------> ✔️ -
Add Installer --------------------> Install from adoptium.net
-
Version --------------------------> jdk-17.0.19+9
Procedure - Docker:
Scroll down and search for Docker installations
Click on Add Docker
Enter or select the following:
-
Name -----------------------------> docker
-
Install automatically
?------------> ✔️ -
Add Installer --------------------> Download from docker.com
-
Docker version
?--------------------------> latest
Procedure - SonarQube Scanner:
Scroll down and search for SonarQube Scanner installations
Click on Add SonarQube Scanner
Enter or select the following:
-
Name -----------------------------> sonar-scanner
-
Install automatically
?------------> ✔️ -
Version --------------------------> latest(SonarQube Scanner 5.0.1.3006)
Procedure - Maven:
Scroll down and search for Maven installations
Click on Add Maven
Enter or select the following:
-
Name -----------------------------> maven3
-
Install automatically
?------------> ✔️ -
Version --------------------------> 3.6.1 or latest
Click on Apply to save configuration.
SonarQube is a code quality assurance tool that collects and analyzes source code, providing reports for the code quality of our project.
It enables us to deploy clean code consistently and reliably.
The sonarqube machine will be bootstrapped using Ansible.
cd boardgame-devops-pipeline-project/bootstrap/
ansible-playbook bootstrap.yml --limit sonarqubeOnce the bootstrap is complete, you can log in as odennav-admin.
Confirm SSH access to the sonarqube node
ssh odennav-admin@10.33.100.5Install Docker
Run the ansible playbook to install docker in the nexus machine
cd boardgame-devops-pipeline-project/docker/
ansible-playbook install-docker.yml --limit sonarqubeInstall SonarQube Container
We'll run the long term community version of sonarqube's image.
docker run -d --name sonar -p 9000:9000 sonarqube:lts-communityConfirm container is running
docker ps -a --filter "name=sonar"Browse to UI of SonarQube at 10.33.100.5:9000
Use admin for default username and password. Update to new password when requested.
Generate Token
Go to Administration tab and select Security tab.
From the drop down, click on Users.
This section is used to create and administer individual users.
Click on button at far right under Tokens column.
Enter Token Name as sonar-token and click on Generate. Note period of expiry.
Copy this access code, we'll use it to create credential for SonarQube in jenkins.
Create SonarQube Secret for Jenkins
Go to Jenkins Dashboard and select Manage Jenkins.
Under the Security section, select Credentials
Click on (global) domain of jenkins System store.
Next, click on blue button + Add Credentials at the top right.
Assign the following:
Kind -------------------------> Secret text
Name ------------------------- > sonar-token
Scope ------------------------> Global
Secret -----------------------> sonar-token
Description ------------------> sonar-token generated
Configure SonarQube Server
Go to Jenkins Dashboard and select Manage Jenkins.
Under the System Configuration section, select Configure System.
Scroll down and search for SonarQube servers and SonarQube installatons
Click Add SonarQube and assign the following:
Name ---------------------------------> sonar
Server URL ---------------------------> https://10.33.100.5:9000
Server authentication token ----------> sonar-token
Click on Apply and Save.
SonarQube QualityGate Setup
Go to Administration tab and select Configuration drop-down tab.
From the drop down, click on Webhooks.
This section is used to create webhooks used to notify external services when a project analysis is done.
Click the Create button at top-right section.
To create webhook, assign the following:
Name -------------------------> jenkins
URL --------------------------> https://10.33.100.7:8080/sonarqube-webhook/
Secret -----------------------> ''
Save this webhook. It will be used in Jenkins pipeine script
Bootstrap Nexus Node
The nexus machine will be bootstrapped using Ansible.
cd boardgame-devops-pipeline-project/bootstrap/
ansible-playbook bootstrap.yml --limit nexusOnce the bootstrap is complete, you can log in as odennav-admin.
Confirm SSH access to the nexus node
ssh odennav-admin@10.33.100.6Install Docker
Run the ansible playbook to install docker in the nexus machine
cd boardgame-devops-pipeline-project/docker/
ansible-playbook install-docker.yml --limit nexusRun Nexus Container with the latest version of nexus3 docker image.
docker run -d --name Nexus -p 8081:8081 sonatype/nexus3Confirm container is running
docker ps -a --filter "name=Nexus"Browse to UI of Nexus at 10.33.100.6:8081
Note, the username to sign into the Sonatype Nexus Repository is admin .
The password is located in /nexus-data/admin.password file in the container.
docker -exec -it Nexus /bin/bash
cat sonatyp-work/nexus-data/admin.passwordCopy the password and use it to sign in. Then choose a new password for the admin user.
Sonatype Nexus Artifact Repository Setup
To publish artifacts to Nexus, we'll add the nexus repository urls to the pom.xml file in the source code project.
Add the urls of the following nexus repositories to the pom.xml file:
-
Nexus releases
-
Nexus snapshots
<distributionManagement>
<repository>
<id>maven-releases</id>
<url>http://10.33.100.6:8081/repository/maven-releases/</url>
</repository>
<snapshotRepository>
<id>maven-snapshots</id>
<url>http://10.33.100.6:8081/repository/maven-snapshots/</url>
</snapshotRepository>
</distributionManagement>
Configure Nexus Global Settings Credential
Ensure the Config File Provider plugin is already installed.
When plugins selected are installed, go to Global Tool Configuration under Manage Jenkins section of Jenkins dasboard
Select Managed files and click on + Add a new Config at left tab.
Assign the following:
Type -----------------------------> Global Maven settings.xml
ID of the config file ------------> global-settings
Click Next.
Next, add the following to the end of the configuration file, settings.xml in the Content section
The <servers> section in the settings.xml file specifies the authentication information to use when connecting to the Nexus server.
<server>
<id>maven-releases</id>
<username>admin</username>
<password>password</password>
</server>
<server>
<id>maven-snapshots</id>
<username>admin</username>
<password>password</password>
</server>
Click on Submit
Note the name of this global settings configuration file is MyGlobalSettings.
Personal access token from Github is needed for Jenkins to access source code files.
To create token, implement the following:
-
Click on your profile at the top right, scroll down and click on
:gear: Settings -
Scroll down at left bar and select
<> Developer Settings -
Select the drop down
:key: Personal access tokensand click onTokens (classic) -
Select drop down 'Generate new token
at top right and click onGenerate new token (classic)`
Assign the following:
token name ----------------------------> git-token
Expiration ----------------------------> 30 days
Select scopes -------------------------> Select all scopes except delete permissions
-
Click on
Generate token -
Copy and save token generated. we'll add it as secret credential in Jenkins.
Create Github Secret for Jenkins
Go to Jenkins Dashboard and select :gear: Manage Jenkins.
Under the Security section, select Credentials
Click on (global) domain of jenkins System store.
Next, click on blue button + Add Credentials at the top right.
Assign the following:
Kind -------------------------> Username with password
Scope ------------------------> Global
Username ---------------------> odennav
ID -----------------------> git-cred
Description ------------------> boardgame-git-repo
Click on Create.
Trivy is an open source security scanner used to find security vulnerabilities of dependencies used in source code project and Iac misconfigurations.
It can also scan the following:
- Container images, filesystem, virtual machine image, git repository, kubernetes cluster and cloud infrastructure.
Install using apt package manager
Add repository to /etc/apt/sources.list.d in Jenkins machine.
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 -y trivyIf you intend to use trivy as container image, mount docker.sock as from the host into the Trivy container.
Confirm trivy installed
trivy --versionTrivy will be used to scan the filesystem and generate the scan report with html.tpl file in the /boardgame-devops-pipeline-project/trivy/ directory.
Copy the template to Jenkins machine.
sudo mkdir ~/trivy
sudo cd trivy
sudo vi html.tplEnsure Jenkins has permission to use the template
sudo chown -R jenkins:jenkins ~/trivyCreate DockerHub Secret for Jenkins
Go to Jenkins Dashboard and select Manage Jenkins.
Under the Security section, select Credentials
Click on (global) domain of jenkins System store.
Next, click on blue button + Add Credentials at the top right.
Assign the following:
Domain ------------------------> Global credentials
Kind --------------------------> Username with password
Username ----------------------> odennav
Password ----------------------> **********
ID ----------------------------> docker-cred
Description -------------------> dockerhub-cred
Automate email notifications of the jenkins pipeline build results by configuring Jenkins to send emails through an SMTP server. We'll use SMTP server from Gmail.
To give Jenkins permission to access our Google account, we'll need an app password. This is a 16-digit passcode.
Generate your app password with this guide and name the app Jenkins.
Create Gmail Secret Credential for Jenkins
Go to Jenkins Dashboard and select :gear: Manage Jenkins.
Under the Security section, select Credentials
Click on (global) domain of jenkins System store.
Next, click on blue button + Add Credentials at the top right.
Assign the following:
Kind -------------------------> Username with password
Scope ------------------------> Global
Username ---------------------> odennav@gmail.com
Password ---------------------> <gmail app-password>
ID -----------------------> mail-cred
Description ------------------> gmail-cred
Click on Create.
Configure Email Notification in Jenkins
Go to Jenkins Dashboard and select Manage Jenkins.
Under the System Configuration section, select Configure System.
- Scroll down and search for
Extended E-mail Notification
Click Add SonarQube and assign the following:
SMTP server ---------------------> smtp.gmail.com
SMTP Port -----------------------> 465
Click on drop-down Advanced
Credentials ---------------------> mail-cred
Use SSL -------------------------> :check_mark:
- Scroll down and search for
E-mail Notification
Click Add SonarQube and assign the following:
SMTP server ---------------------------------> smtp.gmail.com
SMTP Port ---------------------------> 465
Click on drop-down Advanced
User Name ----------------------> odennav@gmail.com
Password -----------------------> <gmail app-password>
Use SSL -------------------------> :check_mark:
SMTP Port -----------------------> 465
Click on Apply and Save.
Jenkins Pipeline is a suite of plugins which supports continuous integration and continuous delivery operations in Jenkins.
It's an automated expression of our process for building, testing and deploying source code from Github right through to our staging environment.
Setup Pipeline
Implement procedure below:
-
Go to Jenkins main dashboard and click on
New Item -
Name pipeline as
BoardGameand selectPipelineas type of project, then clickOK -
Click on the created job and select
Discard old buildsMax # of builds to keep--------------- > 2 -
Scroll down to the
Pipelinesection in the configuration screen. -
Choose
Pipeline script from SCMand select type of SCM. -
Enter the URL of the Github repository containing the Jenkinsfile.
-
Add credentials of the Github repository which contains the personal access token.
-
Choose the branch to build from, typically
/main -
Specify the path of Jenkinsfile in SCM as
/boardgame-devops-pipeline-project/Jenkinsfile -
Click on
Saveto save this configuration.
To restart Jenkins and apply configuration changes or updates effectively:
-
Navigate to the Jenkins
Dashboardand click onManage Jenkinsin the sidebar. -
Select
Reload Configuration from DiskorRestart Safely.
The pipeline block below defines all the actions required for the secure deployment of the BoardGame site.
pipeline {
agent any
tools{
jdk 'jdk17'
maven 'maven3'
}
environment{
SCANNER_HOME= tool 'sonar-scanner'
TEMPLATE_PATH="@/home/odennav/trivy/html.tpl"
DATE=$(date +"%Y-%m-%d_%H-%M")
}
stages {
stage('Git Checkout') {
steps {
git branch: 'main', credentialsId: 'git-cred', url: 'https://github.com/odennav/boardgame-devops-pipeline-project.git'
}
}
stage('Clear Workspace') {
steps {
sh "cleanWs()"
}
}
stage('Maven Clean Phase') {
steps {
sh "mvn clean"
}
}
stage('Maven Compile Phase') {
steps {
sh "mvn compile"
}
}
stage('Maven Test Phase') {
steps {
sh "mvn test"
}
}
stage('Trivy Filesystem Scan') {
steps {
sh """trivy fs --security-checks vuln,secret,misconfig --format template --template ${TEMPLATE_PATH} --output trivy_fs_report_$DATE.html . """
}
}
stage('SonarQube Scan') {
steps {
withSonarQubeEnv('sonar'){
sh '''$SCANNER_HOME/bin/sonar-scanner -Dsonar.projectName=BoardGame \
-Dsonar.java.binaries=. \
-Dsonar.projectKey=BoardGame '''
}
}
}
stage('SonarQube Quality Gate') {
steps {
script {
waitForQualityGate abortPipeline: false, credentialsId: 'sonar-token'
}
}
}
stage('Maven Build Phase') {
steps {
sh "mvn package"
}
}
stage('Publish Artifacts to Nexus') {
steps {
withMaven(globalMavenSettingsConfig: 'global-settings', jdk: 'jdk17', maven: 'maven3', mavenSettingsConfig:",traceability: true) {
sh "mvn deploy"
}
}
}
stage('Build & Tag Docker Image') {
steps {
script {
withDockerRegistry(credentialsId: 'docker-cred', toolName: 'docker') {
sh "docker build -t odennav/boardgame:v1 ./boardgame"
}
}
}
}
stage('Scan Docker Image') {
steps {
sh """trivy image --security-checks vuln,secret,misconfig --format template --template ${TEMPLATE_PATH} --output trivy_image_scan_report_$DATE.html . """
}
}
stage('Push Docker Image') {
steps {
script {
withDockerRegistry(credentialsId: 'docker-cred', toolName: 'docker') {
sh "docker push odennav/boardgame:v1 "
}
}
}
}
stage('Deploy to Kubernetes Cluster') {
steps {
withKubeConfig(caCertificate:",clusterName: 'kubernetes',contextName:",credentialsId:'k8s-cred',namespace:'boardgame',restrictKubeConfigAccess:false,serverUrl:'https://172.31.8.1.146:6443'){
sh "kubectl apply -f kubernetes-manifests/boardgame-manifests"
{
}
}
stage('Verify Kubernetes Deployments') {
steps {
withKubeConfig(caCertificate:",clusterName: 'kubernetes',contextName:",credentialsId:'k8s-cred',namespace:'boardgame',restrictKubeConfigAccess:false,serverUrl:'https://172.31.8.1.146:6443'){
sh "kubectl get pods"
sh "kubectl get svc"
{
}
}
}
post {
always {
archiveArtifacts artifacts: "trivy_report_*.html", fingerprint: true
publishHTML (target: [
allowMissing: false,
alwaysLinkToLastBuild: false,
keepAll: true,
reportDir: '.',
reportFiles: 'trivy_report_*.html',
reportName: 'Trivy Scan',
])
script {
def jobName = env.JOB_NAME
def buildNumber = env.BUILD_NUMBER
def pipelineStatus = currentBuild.result ?: 'UNKNOWN'
def bannerColor = pipelineStatus.toUpperCase() == 'SUCCESS' ? 'green' : 'red'
def body = """
<html>
<body>
<div style="border: 4px solid ${bannerColor}; padding: 10px;">
<h2>${jobName} - Build ${buildNumber}</h2>
<div style="background-color: ${bannerColor}; padding: 10px;">
<h3 style="color: white;">Pipeline Status: ${pipelineStatus.toUpperCase()}</h3>
</div>
<p>Check the <a href="${BUILD_URL}">console output</a>.</p>
</div>
</body>
</html>
"""
emailext (
subject: "${jobName} - Build ${buildNumber} - ${pipelineStatus.toUpperCase()}",
body: body,
to: 'odennav@gmail.com',
from: 'jenkins@example.com',
replyTo: 'jenkins@example.com',
mimeType: 'text/html',
attachmentsPattern: 'trivy_image_scan_report_$DATE.html'
)
}
}
}
}
Build Pipeline
Select drop-down of BoardGame pipeline created above and trigger a build of pipeline job.
-
Click on
Build Now -
Jenkins will fetch the Jenkinsfile from the Github repository and run the jobs defined.
-
View the progress of the pipeline job on the Jenkins dashboard.
-
Click on the job to view detailed logs and status updates as each stage of the pipeline is executed.
Check the console output and logs for more info on any failures.
SAST Reports
To view reports generated by SonarQube:
-
Browse to
10.33.100.5:9000and click onProjectstab. -
Select project we created in pipeline script,
BoardGame. -
View bugs, vulnerabilities, code smells, duplications and hotspots reviews.
To view scan reports from Trivy:
-
Select pipeline job created
-
Scroll down and click on
Trivy Scanin Jenkins. -
View vulnerabilities found with different severity levels.
Install Prometheus
Download Prometheus
cd ~
wget https://github.com/prometheus/prometheus/releases/download/v2.53.0/prometheus-2.53.0.linux-amd64.tar.gzVerify the SHA256 checksum
sha256sum prometheus-2.53.0.linux-amd64.tar.gzExtract prometheus from the tarball file
tar -xzvf prometheus-2.53.0.linux-amd64.tar.gzMove prometheus binary to bin directory
sudo mv ./prometheus-2.53.0.linux-amd64 /usr/local/bin/Browse to prometheus UI on port 9090.
Manage Prometheus with Systemd
We'll create a system service for Prometheus. This enables us to manage it efficiently.
Create a service file for prometheus service
sudo touch /etc/system/service/prometheus.serviceAdd this config to the prometheus.service file
[Unit]
Description=Prometheus Monitoring System
Wants=network-online.target
After=network-online.target
[Service]
User=prometheus
Group=prometheus
Type=simple
ExecStart=/usr/local/bin/prometheus-2.53.0.linux-amd64/prometheus
Restart=on-failure
[Install]
WantedBy=multi-user.targetCreate Prometheus user and group
sudo adduser --no-create-home --shell /bin/false prometheusEnable new ownership for prometheus
sudo chown -R prometheus:prometheus /usr/local/bin/prometheus-2.53.0.linux-amd64Reload system to recognize new service created
sudo systemctl daemon-reloadStart the prometheus service
sudo systemctl start prometheusEnable the service to start on boot
sudo systemctl enable prometheus
Confirm the status of prometheus service
sudo systemctl status prometheusInstall Grafana
Download and install the grafana
cd ~
sudo apt-get install -y adduser libfontconfig1 musl
wget https://dl.grafana.com/enterprise/release/grafana-enterprise_11.0.0_amd64.deb
sudo dpkg -i grafana-enterprise_11.0.0_amd64.debStart the grafana-server
sudo /bin/systemctl start grafana-serverBrowse to grafana UI on port 3000.
Log in with the username as admin and the password as admin.
Install Blackbox Exporter
Download the blackbox exporter
cd ~
wget https://github.com/prometheus/blackbox_exporter/releases/download/v0.25.0/blackbox_exporter-0.25.0.linux-amd64.tar.gzVerify the SHA256 checksum
sha256sum blackbox_exporter-0.25.0.linux-amd64.tar.gzExtract blackbox exporter from the tarball file
tar -xzvf blackbox_exporter-0.25.0.linux-amd64.tar.gzMove the blackbox exporter to the bin diectory
sudo mv ./blackbox_exporter-0.25.0.linux-amd64 /usr/local/binManage Blackbox Exporter with Systemd
We'll create a system service for Blackbox exporter. This enables us to manage it efficiently.
Create a service file for blackbox_exporter service
sudo touch /etc/system/service/blackbox_exporter.serviceAdd this config to the blackbox_exporter.service file
[Unit]
Description=Blackbox Exporter
Wants=network-online.target
After=network-online.target
[Service]
User=blackbox
Group=blackbox
Type=simple
ExecStart=/usr/local/bin/blackbox_exporter-0.25.0.linux-amd64/blackbox_exporter
Restart=on-failure
[Install]
WantedBy=multi-user.targetCreate blackbox user and group
sudo adduser --no-create-home --shell /bin/false blackboxEnable new ownership of blackbox binary
sudo chown -R prometheus:prometheus /usr/local/bin/blackbox_exporter-0.25.0.linux-amd64Reload system to recognize new service created
sudo systemctl daemon-reloadStart the prometheus service
sudo systemctl start blackbox_exporterEnable the service to start on boot
sudo systemctl enable blackbox_exporter
Confirm the status of prometheus service
sudo systemctl status blackbox_exporterBrowse to grafana UI on port 9115.
Setup Blackbox Exporter
Pass Boardgame site as a target for the blackbox exporter. Obtain the external IPv4 address assigned by Metallb
kubectl get svc --all-namespaces -o wideAdd the config below to prometheus-2.53.0.linux-amd64/prometheus.yml file.
scrape_configs:
- job_name: 'blackbox'
metrics_path: /probe
params:
module: [http_2xx] # Look for a HTTP 200 response.
static_configs:
- targets:
- http://prometheus.io # Target to probe with http.
- http://<load balanced external-ip>:<port> # Target to probe with http
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: 10.33.100.8:9115 # The blackbox exporter's real hostname and port.Restart prometheus
sudo systemctl restart prometheusCreate Grafana Data Source
Open a web browser and connect to 10.33.100.8:3000.
Recall the username is admin and the password is admin
Implement the following:
Click on the ⚙️ icon in the menu bar on the left. It will take you to the Configuration page for Grafana.
Click on the Add data sources button
Click on Prometheus as your choice of open-source time series database.
Assign the following as below:
Name ----------------> prometheus
Default--------------> Click to turn the selector on.
URL -----------------> http://10.33.100.8:9090/
Click the Save & Test button.
Now Grafana can access the metrics from Prometheus.
Import System Dashboard
Hover over the + sign in the menu on the left of your screen. It will expand into a menu when you hover over it.
From there, click on Import.
Implement the following:
In the Grafana dashboard URL or id field, enter 7587 and click the Load button next to it.
On the next screen in the Select a Prometheus data source box, select prometheus.
Then click on the Import button.
Now you should see a dashboard displaying scraped Prometheus metrics for the Boardgame website.
Ensure to save the imported dashboard.
Enjoy!

