Leveraging a multi-container Pod in Jenkins means that we can use external tools like OWASP Zed Attack Proxy. Instead of installing ZAP into each and every potential Jenkins agent container (Maven, Gradle, NPM, etc...), we can use the sidecar pattern to run ZAP alongside whatever build container we would normally used without any changes.
The goal of achieving continuous deployment/delivery is one which many feel uncomfortable with. How can we be assured that the product which is being deployed meets our standards for quality, reliability, and (sometimes most importantly) security? Each stage we add to a CI/CD pipeline is intended to give use further comfort and assurance that our deployed release is up to those standards; but how can we ensure that we are doing all that is needed to analyze for security vulnerabilities? Enter OWASP Zed Attack Proxy, which as it's name suggests is an HTTP Proxy server which also analyzes all requests which pass through for a multitude of security threats. In addition, it can be made to perform various attacks against the target application so that we can be assured that our applications are able to withstand the onslaught of malicious actions on the Internet. In this session, we will show how you can deploy ZAP in OpenShift, integration ZAP with Jenkins (or other) pipelines, capture reports, configure custom attacks and break the pipeline in case standards are not met.
How To Configure Multiple Containers
- A running OpenShift cluster with access to the Internet (By default, though it can be reconfigured to work in a disconnected environment)
- A ZAP Jenkins Agent built in OpenShift
- A build container for the appropriate project (Maven, NPM, etc...)
Once you have used the instructions for setting up the multiple containers in the Jenkins pipeline script, you can then use the stages shown below to integrate ZAP
stage('Integration And Penetration Testing') {
parallel { // <1>
stage('Start ZAP') {
options {
timeout(time: 10, unit: 'MINUTES')
}
steps {
container('jenkins-slave-zap') {
dir('/tmp/workspace') {
sh returnStatus: true, script: '/zap/zap.sh -daemon -host 0.0.0.0 -port 9080 -config api.addrs.addr.name=.* -config api.addrs.addr.regex=true -config api.disablekey=true'
}
}
}
}
// Implement stage to run Integration Tests Here
stage('Integration/Acceptance Tests') {
steps {
script {
def testApiHost = ""
openshift.withCluster() { // <2>
openshift.withProject(env.TEST) {
def routeDef = openshift.selector("route", env.APP_NAME).object()
testApiHost = "${routeDef.spec.host}"
}
}
retry(10) { // <3>
sleep 5
sh 'curl -s http://127.0.0.1:9080/JSON/core/view/mode'
}
dir(".mvn") { // <4>
sh "echo '-Dhttp.proxyHost=127.0.0.1 -Dhttp.proxyPort=9080' > jvm.config"
}
def jsonReport = "{}"
withEnv([ "INTEGRATION_TEST_HOST=http://${testApiHost}/v1"]) {
try { // <5>
sh 'mvn -T 1.5C -Pintegration-testing failsafe:integration-test failsafe:verify'
sh 'mkdir zap-report'
} catch (Exception e) {
throw e
} finally {
// Shut down ZAP and capture reports
sleep 5
sh 'curl -v -o ./zap-report/zap-report.html http://127.0.0.1:9080/OTHER/core/other/htmlreport'
jsonReport = sh(returnStdout: true, script: 'curl http://127.0.0.1:9080/OTHER/core/other/jsonreport')
sh 'curl http://127.0.0.1:9080/JSON/core/action/shutdown'
dir(".mvn") {
sh "rm -f jvm.config"
}
}
}
// <6>
def jsonData = new JsonSlurper().parseText(jsonReport)
def highCriticalRisks = jsonData.site.each { site ->
site.alerts.each { alert ->
def alertValue = alert.riskcode as Integer
if (alertValue >= 3) {
error 'High/Critical Risks Detected By Zed Attack Proxy'
}
}
}
}
}
}
}
}
// ... SNIP
post {
always { // <7>
// publish ZAP report
publishHTML(target: [
reportDir : "zap-report",
reportFiles : 'zap-report.html',
reportName : 'Zed Attack Proxy Penetration Testing Report',
keepAll : true,
alwaysLinkToLastBuild: true,
allowMissing : true
])
}
}
- Run the stages in parallel so that ZAP will come up and stay running while the intergration tests run
- Get the URL for the integration test target using the OpenShift DSL
- Wait for ZAP to start and be ready for processing before starting the integration tests
- Set up a Maven JVM config file so that the proxy properties can be set for the JVM
- This can be customized for other build systems like NPM or use the Linux proxy environment variables
- Run the integration tests inside a
try/catch/finally
so that in case of failure the ZAP instance can be shut down - Process the JSON results from ZAP in order to determine the levels issues detected and fail the build if the level is above the desired threshold
- In a
post
block under thealways
section, use thepublishHTML
plugin to publish the report as a Jenkins artifact.