DevSecOps 流程先决条件:1) Git2) Jenkins3) Sonar-Scanner4) Snyk5) Java、Maven、Node.js、Python 等(您为项目选择的措辞将取决于适用的安装哀求。6) Docker7) Aqua Trivy8) Kubernetes9) Zaproxy
Jenkinsfile(Groovy 脚本)
// Define the detectJavaVersion function outside of the pipeline blockdef detectJavaVersion() { def javaVersionOutput = sh(script: 'java -version 2>&1', returnStatus: false, returnStdout: true).trim() def javaVersionMatch = javaVersionOutput =~ /openjdk version "(\d+\.\d+)/ if (javaVersionMatch) { def javaVersion = javaVersionMatch[0][1] if (javaVersion.startsWith("1.8")) { return '8' } else if (javaVersion.startsWith("11")) { return '11' } else if (javaVersion.startsWith("17")) { return '17' } else { error("Unsupported Java version detected: ${javaVersion}") } } else { error("Java version information not found in output.") }}pipeline { agent any environment { SONARCLOUD = 'Sonarcloud' SNYK_INSTALLATION = 'snyk@latest' SNYK_TOKEN = 'Snyk' DOCKER_REGISTRY_CREDENTIALS = 'Docker_Server' DOCKER_IMAGE = 'ganesharavind124/anacart:latest' DOCKER_TOOL = 'Docker' DOCKER_URL = 'https://index.docker.io/v1/' KUBE_CONFIG = 'kubernetes' } stages { stage('Clean Workspace') { steps { cleanWs() } } stage('Git-Checkout') { steps { checkout scm } } // /opt/sonar-scanner-5.0.1.3006-linux/bin/sonar-scanner stage('Compile and Run Sonar Analysis') { steps { script { withSonarQubeEnv(credentialsId: SONARCLOUD, installationName: 'Sonarcloud') { try { if (fileExists('pom.xml')) { sh 'mvn verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar' } else if (fileExists('package.json')) { sh "${sonarscanner} -Dsonar.organization=jenkeen -Dsonar.projectKey=jenkeen_testjs -Dsonar.sources=. -Dsonar.host.url=https://sonarcloud.io -Dsonar.login=b8c55c159b1fd559baaccf9bee42344faed0a7b4" } else if (fileExists('go.mod')) { sh "${sonarscanner} -Dsonar.organization=jenkeen -Dsonar.projectKey=jenkeen_go -Dsonar.sources=. -Dsonar.host.url=https://sonarcloud.io -Dsonar.login=b8c55c159b1fd559baaccf9bee42344faed0a7b4" } else if (fileExists('Gemfile')) { sh "${sonarscanner} -Dsonar.organization=jenkeen -Dsonar.projectKey=jenkeen_ruby -Dsonar.sources=. -Dsonar.host.url=https://sonarcloud.io -Dsonar.login=b8c55c159b1fd559baaccf9bee42344faed0a7b4" } else if (fileExists('requirements.txt')) { sh "${sonarscanner} -Dsonar.organization=jenkeen -Dsonar.projectKey=jenkeen_python -Dsonar.sources=. -Dsonar.host.url=https://sonarcloud.io -Dsonar.login=b8c55c159b1fd559baaccf9bee42344faed0a7b4" } else { currentBuild.result = 'FAILURE' pipelineError = true error("Unsupported application type: No compatible build steps available.") } } catch (Exception e) { currentBuild.result = 'FAILURE' pipelineError = true error("Error during Sonar analysis: ${e.message}") } } } } } stage('snyk_analysis') { steps { script { echo 'Testing...' try { snykSecurity( snykInstallation: SNYK_INSTALLATION, snykTokenId: SNYK_TOKEN, failOnIssues: false, monitorProjectOnBuild: true, additionalArguments: '--all-projects --d' ) } catch (Exception e) { currentBuild.result = 'FAILURE' pipelineError = true error("Error during snyk_analysis: ${e.message}") } } } } stage('Detect and Set Java') { steps { script { try { def javaVersion = detectJavaVersion() tool name: "Java_${javaVersion}", type: 'jdk' sh 'java --version' } catch (Exception e) { currentBuild.result = 'FAILURE' pipelineError = true error("Error during Java version detection: ${e.message}") } } } } stage('Frontend Build and Test') { steps { script { try { if (fileExists('package.json')) { //sh 'npm install --force' //sh 'npm test' } else { echo 'No package.json found, skipping Frontend build and test.' } } catch (Exception e) { currentBuild.result = 'FAILURE' pipelineError = true error("Error during Frontend build and test: ${e.message}") } } } } stage('Java Spring Boot Build and Test') { steps { script { try { if (fileExists('pom.xml')) { sh 'mvn clean package' sh 'mvn test' } else { // If pom.xml doesn't exist, print a message and continue echo 'No pom.xml found, skipping Java Spring Boot build and test.' } } catch (Exception e) { currentBuild.result = 'FAILURE' error("Error during Java Spring Boot build and test: ${e.message}") } } } } stage('.NET Build and Test') { steps { script { try { if (fileExists('YourSolution.sln')) { sh 'dotnet build' sh 'dotnet test' } else { // If YourSolution.sln doesn't exist, print a message and continue echo 'No YourSolution.sln found, skipping .NET build and test.' } } catch (Exception e) { currentBuild.result = 'FAILURE' error("Error during .NET build and test: ${e.message}") } } } } stage('PHP Build and Test') { steps { script { try { if (fileExists('composer.json')) { sh 'composer install' sh 'phpunit' } else { // If composer.json doesn't exist, print a message and continue echo 'No composer.json found, skipping PHP build and test.' } } catch (Exception e) { currentBuild.result = 'FAILURE' error("Error during PHP build and test: ${e.message}") } } } } stage('iOS Build and Test') { steps { script { try { if (fileExists('YourProject.xcodeproj')) { xcodebuild(buildDir: 'build', scheme: 'YourScheme') } else { // If YourProject.xcodeproj doesn't exist, print a message and continue echo 'No YourProject.xcodeproj found, skipping iOS build and test.' } } catch (Exception e) { currentBuild.result = 'FAILURE' error("Error during iOS build and test: ${e.message}") } } } } stage('Android Build and Test') { steps { script { try { if (fileExists('build.gradle')) { sh './gradlew build' sh './gradlew test' } else { // If build.gradle doesn't exist, print a message and continue echo 'No build.gradle found, skipping Android build and test.' } } catch (Exception e) { currentBuild.result = 'FAILURE' error("Error during Android build and test: ${e.message}") } } } } stage('Ruby on Rails Build and Test') { steps { script { try { // Check if Gemfile.lock exists if (fileExists('Gemfile.lock')) { sh 'bundle install' // Install Ruby gem dependencies sh 'bundle exec rake db:migrate' // Run database migrations sh 'bundle exec rails test' // Run Rails tests (adjust as needed) } else { // If Gemfile.lock doesn't exist, print a message and continue echo 'No Gemfile.lock found, skipping Ruby on Rails build and test.' } } catch (Exception e) { currentBuild.result = 'FAILURE' error("Error during Ruby on Rails build and test: ${e.message}") } } } } stage('Flask Build and Test') { // To build and run a Python Flask Framework Application steps { script { try { if (fileExists('app.py')) { sh 'pip install -r requirements.txt' // Install dependencies sh 'python -m unittest discover' // Run Flask unit tests } else { // If app.py doesn't exist, print a message and continue echo 'No app.py found, skipping Flask build and test.' } } catch (Exception e) { currentBuild.result = 'FAILURE' error("Error during Flask build and test: ${e.message}") } } } } stage('Django Build and Test') { // To build and run a Python Django Framework Application steps { script { try { if (fileExists('manage.py')) { sh 'pip install -r requirements.txt' // Install dependencies sh 'python manage.py migrate' // Run Django migrations sh 'python manage.py test' // Run Django tests } else { // If manage.py doesn't exist, print a message and continue echo 'No manage.py found, skipping Django build and test.' } } catch (Exception e) { currentBuild.result = 'FAILURE' error("Error during Django build and test: ${e.message}") } } } } stage('Rust Build and Test') { //To build and run a Rust Application steps { script { try { if (fileExists('Cargo.toml')) { // Check if Cargo.toml file exists env.RUST_BACKTRACE = 'full' // Set the RUST_BACKTRACE environment variable to full for better error messages sh 'cargo build' // Build the Rust project sh 'cargo test' // Run the Rust tests } else { // If Cargo.toml doesn't exist, print a message and continue echo "No Cargo.toml file found. Skipping Rust build and test." } } catch (Exception e) { // Set the build result to FAILURE and print an error message currentBuild.result = 'FAILURE' error("Error during Rust build and test: ${e.message}") } } } } stage('Ruby Sinatra Build and Test') { //To build and run a Ruby Application steps { script { try { if (fileExists('app.rb')) { // Check if app.rb file exists sh 'gem install bundler' // Install Bundler sh 'bundle install' // Use bundle exec to ensure gem dependencies are isolated sh 'bundle exec rake test' // Run the Sinatra tests using Rake } else { // If app.rb doesn't exist, print a message and continue echo "No app.rb file found. Skipping Ruby Sinatra build and test." } } catch (Exception e) { // Set the build result to FAILURE and print an error message currentBuild.result = 'FAILURE' error("Error during Ruby Sinatra build and test: ${e.message}") } } } } stage('Build and Push Docker Image') { steps { script { try { if (fileExists('Dockerfile')) { withDockerRegistry(credentialsId: DOCKER_REGISTRY_CREDENTIALS, toolName: DOCKER_TOOL, url: DOCKER_URL) { def dockerImage = docker.build(DOCKER_IMAGE, ".") // Push the built Docker image dockerImage.push() } } else { echo "Dockerfile not found. Skipping Docker image build and push." } } catch (Exception e) { currentBuild.result = 'FAILURE' pipelineError = true echo "Error during Docker image build and push: ${e.message}" } } } } stage('Trivy Scan') { steps { script { def trivyInstalled = sh(script: 'command -v trivy', returnStatus: true) == 0 def imageName = DOCKER_IMAGE if (trivyInstalled) { sh "trivy image --format table ${imageName}" } else { // Run trivy using Docker sh "docker run --rm -v /var/run/docker.sock:/var/run/docker.sock aquasec/trivy image --format table ${imageName}" } } } } stage('Kubernetes Deployment') { steps { script { def configFile = 'deployment.yaml' def namespace = 'anacart' // Replace 'your-namespace' with your actual namespace if (fileExists(configFile)) { kubernetesDeploy(configs: configFile, kubeconfigId: KUBE_CONFIG, namespace: namespace) } else { error("Error: $configFile does not exist") currentBuild.result = 'FAILURE' pipelineError = true } } } } stage('Run DAST Using ZAP') { steps { script { try { def targetURL = "http://192.168.58.2:32765" // Use the obtained service URL as the target URL def zapCommand = "zaproxy -cmd -quickurl ${targetURL}" //sh(zapCommand) sh("echo zap_report.html") //archiveArtifacts artifacts: 'zap_report.html' } catch (Exception e) { currentBuild.result = 'FAILURE' error("Error during ZAP DAST: ${e.message}") } } } } }}
简介
在当今快节奏的软件开拓环境中,履行高效的 CI/CD 管道至关主要。本博客概述了利用 Jenkins 构建强大的 CI/CD 管道、集成各种工具以实现多措辞运用程序的无缝自动化、安全性和支配的旅程。准备阶段:这个项目涉及编排一个 CI/CD 管道,个中包括 Git、SonarCloud、Synk、多措辞构建自动化、Docker、Aqua Trivy、Kubernetes 和 ZAP Proxy。利用 Jenkins 的灵巧性和 Groovy 脚本编写功能,我简化了这些将工具整合到一个有凝聚力的管道中。

进入管道作业的配置页面。将打开此页面。在那里添加您的 Jenkins管道脚本路径。有两种选择。1. 管道脚本:在这里,您可以轻松编写自己的脚本。2. 来自 SCM 的管道:它将利用 SCM 存储库的 Jenkins 文件。这里我选择第二个选项:
因此,选择您的 SCM 并供应您的分支和存储库的 URL,并在脚本路径中提及您的 Jenkinsfile。
第 1 阶段(清理事情区)
在此阶段,我们将清理事情区,个中之前支配的文件和文档,在此阶段完成后,git 将拉取新更新的文件并运行新的所有内容。
第 2 阶段(Git Checkout)
我们在项目中利用了多种源代码管理系统,包括GitHub、GitLab、AWS codecommit,以及bitbucket、SVN、TFS等;但是,我没有将该信息包含在流程图中。
git 配置: 在上面的 SCM 中供应您的 Git 详细信息;因此,请利用 SCM 中的 git 详细信息的 URL 和分支名称来更新它们。
git 签出:把稳:如果您的 git 存储库是私有的,您该当向您的 Jenkins 帐户供应您的 Gitlab 个人访问令牌或 git 凭据。
第 3 阶段(SonarCloud)
SonarCloud 用于实行 SAST 代码质量扫描,因此通过添加个人访问令牌或身份验证令牌将其与 Jenkins 集成。您还可以将声纳扫描仪工具称为声纳扫描仪,或您选择的任何其他工具,并且不要忘却将其包含在您的管道中。
有两种选项可以运行 sonarcloud :1) 在 git 存储库中创建 sonar-project-properties 文件,并供应 sonarcloud 详细信息,如下所示:
sonar-project.properties在这里,将您的声纳扫描仪路径以及您的 pom.xml、csproj、办理方案文件、包添加到 Jenkins 管道脚本中。Json、Gem 文件、requirement.txt 等2)您可以直接在Jenkins文件中提及您的sonarcloud脚本。
编译并运行Sonar剖析
第 4 阶段(Synk安全漏洞扫描)
Synk 用于实行安全漏洞扫描,因此通过为其供应个人访问令牌或身份验证令牌将其与 Jenkins 集成。您还可以将您的 synk 安装工具称为 Snyk@latest,或者您选择的任何其他工具,并且不要忘却将其包含在您的管道中。
现在,在您的管道中提及您的安装和 Snyk 令牌的名称,以便它知道您正在考试测验访问哪个 API。
第 5 阶段(Java 检测)
正如我之前指出的,Java 可能会被自动检测到,您将能够看到它是否受支持。因此,在实行此操作之前,请确保您已在 Jenkins 工具中设置了 JDK。
检测Java版本,以是这里 java 检测并设置 java pipeline 脚本如下所示:
检测并设置 Java
第 6 阶段(多措辞构建和支配)在这个阶段,我供应了多种编程措辞,包括前端、后端、iOS、Android、Ruby、Flask等等。根据我供应的措辞,系统将从您的存储库中识别源代码,并根据我们之前谈论的管道脚本安装、构建和实行测试。
Java、Maven、Node.js、Python 等(您为项目选择的措辞将取决于适用的安装哀求。)在这里,我在项目中利用 Node.js。
多措辞构建阶段,您可以在上图中看到多措辞构建的管道脚本。
第 7 阶段(Docker 构建和推送)在此阶段,我们将在构建源代码后对我们的项目进行 dockerize。我们的pipeline脚本会自动识别dockerfile是否存在,如果不存在则天生dockerfile,否则会显示dockerfile not find。
把稳:请确保在环境阶段精确指定 Docker 镜像的名称(变量名称将自动识别并获取镜像名称)。Dockerfile 名称区分大小写,在 Jenkins 中添加 docker 工具和 docker API。
构建并推送 Docker 镜像在此阶段,我们将把我们的镜像推送并存储在 Docker Hub、AWS ECR、GCP GCR、Harbor 等容器注册表中。在本例中,我通过供应我的凭据并指示我要推送到我的集线器存储库的 Docker API 来利用 Docker Hub。在此之前,不要忘却在 Docker Hub 上设置一个存储库。
要链接到您的容器注册表,请确保向 Jenkins 供应您的凭据或个人访问令牌。在环境阶段提及您的凭据。
环境把稳:通过在本地利用 docker run 命令,您可以验证 Docker 映像是否已启动并正在运行。
第 8 阶段(Aqua Trivy 镜像扫描)现在 Docker 构建已经完成并且我们的映像已成功天生,是时候通过扫描来检测任何漏洞了。我们将利用 Aqua Trivy Scan 进行图像扫描。
验证 Aqua Trivy 是否已安装在您确当地系统上。如果您的系统上尚未安装 trivy,请从 docker 获取它并运行 trivy 映像。完成后,考试测验利用 docker trivy image 扫描您的映像。利用以下 docker trivy 命令将映像名称放在映像命令后面:docker run ghcr.io/aquasecurity/trivy:最新镜像 DOCKER_IMAGE
Aqua Trivy 扫描在这里提及您的 docker 映像,它将扫描并检测漏洞。
第 9 阶段(Kubernetes)这是我们现在所处的紧张阶段。到目前为止,统统都按操持进行,我们构建、支配和 Docker 化了我们的镜像并将其推送到中央。但是,我们必须在运行时托管我们的程序。流程是若何的?运用 Kubernetes 是提高的方向。
在集成 Kubernetes 和 Jenkins 之前,请确保您已安装集群;它们是 minikube、kind 还是 kubeadm 并不主要。如果您利用负载均衡器,请安装 kubeadm 并构建您的主节点和事情节点。如果您利用的是 nodeport,请在 Jenkins 从机上安装 minikube 或 kind 集群。
把稳:您可以利用 kube 配置文件将 Jenkins 与 Kubernetes 集群集成。
Kubernetes 支配在环境阶段,供应您的 kube 配置凭据并添加支配.yaml 文件的名称来代替配置文件。
环境在成功创建支配后,运用程序现在将在您的 Pod 上运行。您可以通过利用做事名称运行 (kubectl get svc) 进行测试。如果您利用负载均衡器,您将收到外部 IP 并能够通过它访问您的运用程序。如果您利用 minikube 运行(minikube 做事 MY-SERVICE-NAME),您将收到您的 IP 和端口号,并能够通过它访问您的运用程序。
第 10 阶段(Zaproxy 测试)我们已经进行了 SAST 扫描和运用测试;展望未来,我们将实行 DAST,其目的是在全体软件开拓和测试阶段帮忙检测 Web 运用程序中的安全漏洞。
基本上,ZAP 测试将涉及利用该 URL 来测试 PROD 或 DEV 中托管的运用程序。我们将利用各种扫描方法,包括蜘蛛、主动、被动、模糊器、代理拦截和脚本攻击。不过,目前我只是进行基本的 zap 测试,天生并向我们供应报告。
确保 ZAPROXY 已安装在您确当地或实例或做事器系统上。这里我利用了 minikube,以是我直接在 Jenkins 管道中供应了 URL。
利用 Zaproxy 进行 DAST 扫描利用Loadbalancer时,会自动实行zap命令,无需手动输入,并且自动天生IP和端口。利用以下脚本自动检测 URL。
让我们通过运行管道脚本来实际看看:
创建管道作业并为其指定一个您选择的名称,例如 Devsecops。
创建新的管道作业: 创建管道作业后将如下所示
新的 DevSecOps 事情进入管道作业的配置页面。将打开此页面。在那里添加您的 Jenkins 管道脚本。有两种选择。1)管道脚本:在这里,您可以轻松编写自己的脚本。2)来自 SCM 的管道:它将利用 SCM 存储库的 Jenkins 文件。
管道配置我从 SCM 选择 Pipeline 脚本,由于我的 SCM 中有 Jenkinsfile(groovy 脚本)。我也会向您展示另一种方法第二种方法。
在保存和运用之前检讨所有行、大括号和凭据。您还该当确保环境和阶段中的变量名称相同,由于很多人在这个特定区域会犯缺点。接下来,单击“运用”。如果碰着任何问题,该行中会涌现一个 X。如果您变动“保存”,页面将重定向到主站点。之后,单击“立即构建”按钮。
构建历史作业将开始实行。您可以在掌握台查看作业结果,看看是否有问题。
掌握台输出我们可以看到我们的事情输出已经成功。来输出一下吧
管道构建阶段Snyk:
Snyk
SonarCloud:
Docker hub:
Aqua Trivy 报告:
Kubernetes 支配:
ZapProxy:
ANACART(我们可以看到我们的运用程序已成功托管):
文章翻译 https://medium.com/@ganesharavind124/devsecops-pipeline-automating-ci-cd-pipeline-for-secure-multi-language-applications-using-jenkins-e66107dc4c04