diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 3d7fd4b..c14dd26 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,4 +1,4 @@
-name: build Maven
+name: CI/CD Pipeline with DevSecOps
on:
pull_request:
@@ -10,34 +10,101 @@ on:
- reopened
jobs:
- test_and_build:
+ # Build and Test
+ build_and_test:
runs-on: ubuntu-latest
+ steps:
+ - name: Checkout Repository
+ uses: actions/checkout@v4
+
+ - name: Set up Java
+ uses: actions/setup-java@v4
+ with:
+ java-version: '23'
+ distribution: 'temurin'
+
+ - name: Build and Test with Maven
+ run: |
+ mvn clean install
+ mvn test
+ - name: Create JAR File
+ run: mvn clean package
+
+ - name: Upload Build Artifact
+ uses: actions/upload-artifact@v3
+ with:
+ name: build-artifact
+ path: target/*.jar
+
+ static_code_analysis:
+ runs-on: ubuntu-latest
+ needs: build_and_test
steps:
- - name: Checkout the repository
- uses: actions/checkout@v4
+ - name: Checkout Repository
+ uses: actions/checkout@v4
- - name: Set up Java 23
- uses: actions/setup-java@v4
- with:
- java-version: '23'
- distribution: 'temurin'
+ - name: Set up Java
+ uses: actions/setup-java@v4
+ with:
+ distribution: 'temurin'
+ java-version: '23'
- - name: Install dependencies and run tests
- run: mvn clean install
+ - name: Build the project
+ run: mvn clean install -DskipTests
- - name: Run tests with Maven
- run: mvn test
+ - name: Cache SonarQube packages
+ uses: actions/cache@v4
+ with:
+ path: ~/.sonar/cache
+ key: ${{ runner.os }}-sonar
+ restore-keys: ${{ runner.os }}-sonar
+ - name: Cache Maven packages
+ uses: actions/cache@v4
+ with:
+ path: ~/.m2
+ key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
+ restore-keys: ${{ runner.os }}-m2
+ - name: Build and analyze
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
+ run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=yashsahsani_Learning-SpringBoot
- - name: create jar file
- run: mvn clean package
-
- create-docker:
+
+ # Security Scanning
+ security_scanning:
runs-on: ubuntu-latest
- needs: test_and_build
+ needs: build_and_test
+ steps:
+ - name: Checkout Repository
+ uses: actions/checkout@v4
+
+ - name: Snyk Dependency Scan
+ uses: snyk/actions/setup@master
+ - name: Run Snyk Test
+ run: snyk test --all-projects --severity-threshold=high
+ env:
+ SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
+
+ - name: Monitor the project on Snyk
+ run: snyk monitor --all-projects
+ env:
+ SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
+
+ - name: Scan for Vulnerable Dependencies with Trivy
+ uses: aquasecurity/trivy-action@master
+ with:
+ scan-type: 'fs'
+ severity: 'CRITICAL,HIGH'
+ ignore-unfixed: true
+ # Docker Image Build and Scan
+ docker_build_and_scan:
+ runs-on: ubuntu-latest
+ needs: [build_and_test, security_scanning,static_code_analysis]
steps:
- - name: Checkout the repository
+ - name: Checkout Repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
@@ -48,9 +115,24 @@ jobs:
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- - name: Build Docker image
+
+ - name: Build Docker Image
run: docker build -t ${{ secrets.DOCKER_USERNAME }}/spring-learning:${{ github.sha }} .
- - name: Push Docker image to Docker Hub
- run: |
- docker push ${{ secrets.DOCKER_USERNAME }}/spring-learning:${{ github.sha }}
\ No newline at end of file
+ # - name: Snyk Docker Image Scan
+ # uses: snyk/actions/docker@master
+ # with:
+ # image: ${{ secrets.DOCKER_USERNAME }}/spring-learning:${{ github.sha }}
+ # args: --severity-threshold=high
+ # env:
+ # SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
+
+ - name: Scan Docker Image with Trivy
+ uses: aquasecurity/trivy-action@master
+ with:
+ image-ref: ${{ secrets.DOCKER_USERNAME }}/spring-learning:${{ github.sha }}
+ severity: 'CRITICAL,HIGH'
+ ignore-unfixed: true
+
+ - name: Push Docker Image to Docker Hub
+ run: docker push ${{ secrets.DOCKER_USERNAME }}/spring-learning:${{ github.sha }}
diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml
new file mode 100644
index 0000000..14e192d
--- /dev/null
+++ b/.github/workflows/sonar.yml
@@ -0,0 +1,37 @@
+name: SonarQube
+on:
+ push:
+ branches:
+ - master
+ pull_request:
+ types: [opened, synchronize, reopened]
+jobs:
+ build:
+ name: Build and analyze
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
+ - name: Set up JDK 23
+ uses: actions/setup-java@v4
+ with:
+ java-version: 23
+ distribution: 'temurin' # Alternative distribution options are available.
+ - name: Cache SonarQube packages
+ uses: actions/cache@v4
+ with:
+ path: ~/.sonar/cache
+ key: ${{ runner.os }}-sonar
+ restore-keys: ${{ runner.os }}-sonar
+ - name: Cache Maven packages
+ uses: actions/cache@v4
+ with:
+ path: ~/.m2
+ key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
+ restore-keys: ${{ runner.os }}-m2
+ - name: Build and analyze
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
+ run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=yashsahsani_Learning-SpringBoot
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
index a3a3c82..800d502 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -5,7 +5,7 @@ COPY . .
RUN mvn clean package
# Stage 2: Run the application
-FROM openjdk:23-jdk-slim
+FROM openjdk:24-jdk-slim
WORKDIR /app
COPY --from=builder /app/target/learning-spring.jar app.jar
EXPOSE 8080
diff --git a/kubernetes/configMap.yaml b/kubernetes/configMap.yaml
new file mode 100644
index 0000000..4ff2b71
--- /dev/null
+++ b/kubernetes/configMap.yaml
@@ -0,0 +1,8 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: spring-boot-app-config
+ namespace: spring
+data:
+ DATASOURCE_URL: jdbc:mysql://db-service:3306/learning_spring
+ SPRING_PROFILES_ACTIVE: prod
\ No newline at end of file
diff --git a/kubernetes/db-deploy.yaml b/kubernetes/db-deploy.yaml
index 0072091..7932588 100644
--- a/kubernetes/db-deploy.yaml
+++ b/kubernetes/db-deploy.yaml
@@ -6,7 +6,7 @@ metadata:
labels:
app: db
spec:
- replicas: 1
+ replicas: 2
selector:
matchLabels:
app: db
@@ -20,16 +20,19 @@ spec:
image: mysql:latest
env:
- name: MYSQL_ROOT_PASSWORD
- value: "prodroot"
+ valueFrom:
+ secretKeyRef:
+ name: db-secret
+ key: MYSQL_ROOT_PASSWORD
- name: MYSQL_DATABASE
value: "learning_spring"
ports:
- containerPort: 3306
- # volumeMounts:
- # - name: mysql-data
- # mountPath: /var/lib/mysql
- # volumes:
- # - name: mysql-data
- # persistentVolumeClaim:
- # claimName: mysql-pvc
+ volumeMounts:
+ - name: mysql-data
+ mountPath: /var/lib/mysql
+ volumes:
+ - name: mysql-data
+ persistentVolumeClaim:
+ claimName: mysql-pvc
\ No newline at end of file
diff --git a/kubernetes/db-secrets.yaml b/kubernetes/db-secrets.yaml
new file mode 100644
index 0000000..0c98f1d
--- /dev/null
+++ b/kubernetes/db-secrets.yaml
@@ -0,0 +1,8 @@
+apiVersion: v1
+kind: Secret
+metadata:
+ name: db-secrets
+ namespace: spring
+type: Opaque
+data:
+ MYSQL_ROOT_PASSWORD: cGFzc3dvcmQ=
\ No newline at end of file
diff --git a/kubernetes/secrets.yaml b/kubernetes/secrets.yaml
new file mode 100644
index 0000000..a2f8742
--- /dev/null
+++ b/kubernetes/secrets.yaml
@@ -0,0 +1,9 @@
+apiVersion: v1
+kind: Secret
+metadata:
+ name: spring-boot-app-secret
+ namespace: spring
+type: Opaque
+data:
+ DATASOURCE_USERNAME: cm9vdA==
+ DATASOURCE_PASSWORD: cHJvZHJvb3Q=
diff --git a/kubernetes/spring-deploy.yml b/kubernetes/spring-deploy.yml
index b549036..6e80097 100644
--- a/kubernetes/spring-deploy.yml
+++ b/kubernetes/spring-deploy.yml
@@ -6,7 +6,7 @@ metadata:
labels:
app: spring-boot-app
spec:
- replicas: 1
+ replicas: 4
selector:
matchLabels:
app: spring-boot-app
@@ -18,14 +18,31 @@ spec:
containers:
- name: spring-boot-app
image: yashsahsani/spring-learning:latest
+ securityContext:
+ readOnlyRootFilesystem: true
+ runAsNonRoot: true
+ allowPrivilegeEscalation: false
env:
- name: DATASOURCE_URL
- value: jdbc:mysql://db-service:3306/learning_spring
+ valueFrom:
+ configMapKeyRef:
+ name: spring-boot-app-config
+ key: DATASOURCE_URL
- name: DATASOURCE_USERNAME
- value: root
+ valueFrom:
+ secretKeyRef:
+ name: spring-boot-app-secret
+ key: DATASOURCE_USERNAME
- name: DATASOURCE_PASSWORD
- value: prodroot
+ valueFrom:
+ secretKeyRef:
+ name: spring-boot-app-secret
+ key: DATASOURCE_PASSWORD
- name: SPRING_PROFILES_ACTIVE
- value: prod
+ valueFrom:
+ configMapKeyRef:
+ name: spring-boot-app-config
+ key: SPRING_PROFILES_ACTIVE
ports:
- - containerPort: 8080
\ No newline at end of file
+ - containerPort: 8080
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 60aa55d..1d7d280 100644
--- a/pom.xml
+++ b/pom.xml
@@ -28,6 +28,8 @@
23
+ yashsahsani
+ https://sonarcloud.io
@@ -37,15 +39,20 @@
-
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-core
+ 11.0.2
+
org.springframework.boot
spring-boot-starter-web
+ 3.4.1
diff --git a/sonar-project.properties b/sonar-project.properties
new file mode 100644
index 0000000..8666443
--- /dev/null
+++ b/sonar-project.properties
@@ -0,0 +1,5 @@
+sonar.projectKey=yashsashani_Learning-SpringBoot
+sonar.organization=yashsahsani
+sonar.host.url=https://sonarcloud.io
+sonar.sources=.
+sonar.java.binaries=target/classes
diff --git a/src/main/java/com/github/learning_spring/controller/DepartmentController.java b/src/main/java/com/github/learning_spring/controller/DepartmentController.java
index b3d6867..c0d689e 100644
--- a/src/main/java/com/github/learning_spring/controller/DepartmentController.java
+++ b/src/main/java/com/github/learning_spring/controller/DepartmentController.java
@@ -52,7 +52,8 @@ public String updateDepartmentById(@PathVariable("id") Long departmentId,@Valid
}
@DeleteMapping("/deleteDepartment/{id}")
- public String deleteMethodName(@PathVariable String id) {
+ public String deleteMethodName(@Valid @PathVariable String id) {
+
return departmentService.deleteDepartment(id);
}