Skip to main content

HOWTO on implementing scanning into a CI Pipeline

Introduction

As a part of the Software Security Assurance guidelines, we are required to perform various types of security scanning.  Security scanning is a specialized kind of testing that attempts to identify potential software bugs that could make a deployed application vulnerable to attack and compromise.  As with any testing, the tests can be manual or automatic, and they wan be performed at various points in the development cycle.   We can classify security scanning into three general categories:

  1. Static Application Security Testing (SAST)  - performed against source code / configuration data a static scan looks for common logic errors that might lead to system compromise.
  2. Dynamic Application Security Testing (DAST) - Performed against a running system, a dynamic scan looks for vulnerable software / configurations that might lead to system compromise.
  3. Security Monitoring - Deployed as a part of the system, a security monitor continuously evaluates the security status relative to a set of rules or policies and reports anomalies.

Static and Dynamic scanning are typically integrated into the development process;  security monitoring is delivered as a feature component of the system.  In the OC-CNE project, we integrating static and dynamic scans into our build pipeline and enforce policies to ensure that we meet the OSSA security guidelines.  The OC-CNE project implements security scans both to ensure the compliance of our delivered software and to provide an example implementation for 5G NF development teams.

Static Scanning

The OC-CNE project currently implements two kinds of static scanning, with other planned for future development.   Static scanning is performed against source repositories or recently built products; these scans do not require a functional deployment/execution environment.

Fortify Scanner (Microfocus.com)

The fortify scanner is one of several OSSA approved scanning tools; the OC-CNE project does not contain any scannable source code and no CI/CD examples have been developed.  For initial releases of 5G NFs, projects shall perform manual scanning using an approved tool; we will explore CI/CD integration in future sprints.

OWASP Dependency Checker (jeremylong/DependencyCheck)

The OWASP Dependency Checker will examine project source and identify known vulnerabilities in 3rd party libraries.  While the OC-CNE project does not contain any scannable source, a simple proof of concept example was created showing how to integrate the OWASP Dependency Checker into your gradle builds.  Here are the details:

Configure gradle to include the OWASP Dependency Check Plug-in:

build.gradle
plugins {
    // Docker plugin 'owasp dependency checker'
    id "org.owasp.dependencycheck" version "4.0.2"
}
  
apply plugin: 'org.owasp.dependencycheck'
dependencyCheck {
    failBuildOnCVSS=7
    cveValidForHours=1
    format='ALL'
    suppressionFile='scanners/owasp_supress.xml'
}

And here is a sample invocation:

$ gradle dependencyCheckAnalyze
> Configure project :
Reckoned version: 0.1.0-beta.4.411+41b3893
> Task :dependencyCheckAnalyze
Verifying dependencies for project occne
Checking for updates and analyzing dependencies for vulnerabilities
Generating report for project occne
Found 0 vulnerabilities in project occne
Deprecated Gradle features were used in this build, making it incompatible with Gradle 6.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/5.1.1/userguide/command_line_interface.html#sec:command_line_warnings
BUILD SUCCESSFUL in 30s
1 actionable task: 1 executed

When run, a full set of the scan details will be generated in the "build" directory.   These include html, cvs, and json reports. The html reports are probably the easiest to view.  If you need to suppress any false alerts, you can view the build/reports/dependency_check_reports.html report and press the "suppress" button to capture suppression file fragment, which you can add to a false alert suppression file.  Here is an example:

OWASP Suppression File
<?xml version="1.0" encoding="UTF-8"?>
   <suppress>
      <notes><![CDATA[
      file name: docker-java-shaded-3.1.0-rc-5-cglib.jar (shaded: com.fasterxml.jackson.core:jackson-databind:2.6.7)
      ]]></notes>
      <gav regex="true">^com\.fasterxml\.jackson\.core:jackson-databind:.*$</gav>
      <cpe>cpe:/a:fasterxml:jackson</cpe>
   </suppress>
   <suppress>
      <notes><![CDATA[
      file name: docker-java-shaded-3.1.0-rc-5-cglib.jar (shaded: com.google.guava:guava:19.0)
      ]]></notes>
      <gav regex="true">^com\.google\.guava:guava:.*$</gav>
      <cpe>cpe:/a:google:guava</cpe>
   </suppress>
   <suppress>
      <notes><![CDATA[
      file name: docker-java-shaded-3.1.0-rc-5-cglib.jar (shaded: com.fasterxml.jackson.core:jackson-databind:2.6.7)
      ]]></notes>
      <gav regex="true">^com\.fasterxml\.jackson\.core:jackson-databind:.*$</gav>
      <cpe>cpe:/a:fasterxml:jackson-databind</cpe>
   </suppress>
</suppressions>

McAfee Malware Scanner (www.mcafee.com)

The McAfee malware scanner use has been mandated by Oracle; the OCCNE team has packaged it as a docker container which can be used to scan selected directories for malware:

McAfee Container Use
# Select the latest scan image to get the latest virus defs
export MCAFEE_IMAGE=cgbu-dev-docker.dockerhub-den.oraclecorp.com/occne/mcafee_scan:latest
 
# To scan a directory other that ${PWD}, adjust the volume selection
docker run -v ${PWD}:/root --rm ${MCAFEE_IMAGE} ./uvscan -v --unzip /root

The artifactory repository (cgbu-dev-docker.dockerhub-den.oraclecorp.com) is used to hold images build by the occne team.

In the OC-CNE project, we build and use this container to scan all of the containers created by our Jenkins pipeline.  Here the Jenkins stage that performed this scan:

StaticScan Jenkins Stage
stage('StaticScan') {
    when { expression { params.SCAN } }
    steps {
        script {
            sh """
                mkdir -p scandir
 
                # extract all of the built images from docker as tar files
                docker save ${env.OS_IMAGE} > scandir/os_image.tar
                docker save ${env.K8S_IMAGE} > scandir/k8s_image.tar
                docker save ${env.CFG_IMAGE} > scandir/cfg_image.tar
                docker save ${env.MCAFEE_IMAGE} > scandir/mcafee_image.tar
 
                # scan all of the built images (clean exit only if no malware found...)
                docker run -v \${PWD}/scandir:/root --rm ${env.MCAFEE_IMAGE} ./uvscan -v --sub --unzip /root
 
                # cleanup
                rm -rf scandir
            """
        }
    }
}

Anchore Engine Vulnerability Scanner (anchore/anchore-engine)

Anchore Engine is a vulnerability scanner that will look a docker containers for known vulnerabilities; the OC-CNE team has added support for anchore-engine scanning in the deployment pipeline as it is in the deployment pipeline that we assemble our list of 3rd party containers (which we then scan).  It should be possible for 5G NFs to scan in their CI pipelines as their container list is well known and container tagging should be computed at that point in time.


Starting the scan container
#!/bin/bash
 
export AEVOLUME=~/scanners/aevolume
 
 
# the IP address of your private repository
export HOSTIP=${HOSTIP:-10.75.216.10}
 
default to the lab proxy
export HTTP_PROXY=${http_proxy:-"http://www-proxy.com:80"}
export HTTPS_PROXY=${https_proxy:-"http://www-proxy.com:80"}
 
if the directory exists, then we assume that the scanner is already running
if [ -d ${AEVOLUME} ]; then
    exit 0
fi
 
mkdir ${AEVOLUME}
cd ${AEVOLUME}
 
docker pull docker.io/anchore/anchore-engine:latest
docker create --name ae docker.io/anchore/anchore-engine:latest
docker cp ae:/docker-compose.yaml ${AEVOLUME}/docker-compose.yaml
 
sed -i \
    -e '/^    environment:/a\ \ \ \ - http_proxy='${HTTP_PROXY}   \
    -e '/^    environment:/a\ \ \ \ - https_proxy='${HTTPS_PROXY} \
    -e '/^    environment:/a\ \ \ \ - no_proxy='${HOSTIP}',localhost,127.0.0.1,us.oracle.com,oracle.com,labs.nc.tekelec.com,oraclecorp.com,engine-catalog,engine-policy-engine,engine-simpleq' \
    docker-compose.yaml
 
docker rm ae
 
# start it up
docker-compose pull
docker-compose up -d



Here is a script that will pull an image from a docker registry and initiate a scan:


Scan an image
#!/bin/bash
 
export IMAGE=${1}
 
this should work but (unfortunately) winterfell maps its own name to loopback rather than static ip
# export HOSTIP=$(ping -c1 ${HOSTNAME} | head -1 | awk -F "[()]" '{print $2}')
 
# name of the private repository host
export HOSTNAME=${HOSTNAME:-$(hostname)}
 
 
# IP address of the private repository host
export HOSTIP=${HOSTIP:-10.75.216.10}
 
 
# port of the private repository
export HOSTPORT=${HOSTPORT:-5000}
 
# where did you start the scanner?
export AEVOLUME=~/scanners/aevolume
export LOG=${AEVOLUME}/AE.log
 
# these are the default credentials
export ANCHORE_CLI_URL=http://localhost:8228/v1
export ANCHORE_CLI_USER=admin
export ANCHORE_CLI_PASS=foobar
 
anchore-cli()
{
    (
        cd ${AEVOLUME}
        docker-compose exec engine-api anchore-cli ${@}
    )
}
 
fiximage()
{
    # the private repository doesn't have DNS resolution - manually resolve it here
    echo $1 | sed "s/^${HOSTNAME}:${HOSTPORT}/${HOSTIP}:${HOSTPORT}/"
}
 
uncomment()
{
    cat $* | grep -v '^[    ]*#' | grep -v '^[  ]*$' | grep -v '^[`][`][`]'
}
scanit()
{
    image=$(fiximage $1)
 
    echo "Scanning: ${image}" >&2
 
    # restore this line if you want to force a rescan
    # anchore-cli image del --force ${image} 2>/dev/null
 
    anchore-cli image add ${image}
    anchore-cli image wait ${image}
    anchore-cli image vuln ${image} all
}
 
scanit ${IMAGE} 2>&1 | tee -a ${LOG}
 
exit 0



And here is an example of the kinds of vulnerabilities that this tool can detect:


Scan Results
$  anchore-cli image  vuln  10.75.191.98:5000/aschnabl/occne/mcafee_scan:latest all
Vulnerability ID        Package                            Severity        Fix                         Vulnerability URL                        
ELSA-2019-0049          libgudev1-219-62.0.1.el7           High            0:219-62.0.4.el7_6.2        http://linux.oracle.com/errata/ELSA-2019-0049.html
ELSA-2019-0049          systemd-219-62.0.1.el7             High            0:219-62.0.4.el7_6.2        http://linux.oracle.com/errata/ELSA-2019-0049.html
ELSA-2019-0049          systemd-libs-219-62.0.1.el7        High            0:219-62.0.4.el7_6.2        http://linux.oracle.com/errata/ELSA-2019-0049.html
ELSA-2019-0201          libgudev1-219-62.0.1.el7           Low             0:219-62.0.4.el7_6.3        http://linux.oracle.com/errata/ELSA-2019-0201.html
ELSA-2019-0201          systemd-219-62.0.1.el7             Low             0:219-62.0.4.el7_6.3        http://linux.oracle.com/errata/ELSA-2019-0201.html
ELSA-2019-0201          systemd-libs-219-62.0.1.el7        Low             0:219-62.0.4.el7_6.3        http://linux.oracle.com/errata/ELSA-2019-0201.html
ELSA-2019-0194          bind-libs-lite-9.9.4-72.el7        Medium          32:9.9.4-73.el7_6           http://linux.oracle.com/errata/ELSA-2019-0194.html
ELSA-2019-0194          bind-license-9.9.4-72.el7          Medium          32:9.9.4-73.el7_6           http://linux.oracle.com/errata/ELSA-2019-0194.html


Dynamic Scanning

Dynamic scanning is performed against the software running in a deployment / execution environment.    The dynamic scans are packaged as a collection of Ansible playbooks delivered in a Docker container image.  The scans can be controlled by passing tags;  the following scans are available:

  • tls - run all the tls related scans
    • ciphers - scans for open ports supporting TLS and confirms OSSA TLS cipher conformance
  • cis - run all the cis related scans
    • docker-bench-security - run the docker cis benchmark scan
    • kube-bench - run the K8s cis benchmark scan
  • scap - run all the scap related scans
    • oval - run the scap oval scan to look for reported vulnerabilities (CVEs)
    • xccd - run all of the xccdf related scans
      • xccdf-standard - run the Oracle Linux 7 Hardening scan
      • xccdf-rht-ccp - run the standard Oracle Linux 7 Cloud Provider Security Scan
      • xccdf-stif-rhel7-disa - run the Oracle Linux 7 DOD Security Scan
All of these scans target a cluster - they use a hosts.ini file to identify cluster hosts and resources and then either run scans on specific cluster hosts or sniff open ports on cluster hosts to perform port based-scanning.

Scan Execution using cne_scan

Like all of the OCCNE tools, cne_scan requires that the environment is defined in a kubespray compatible hosts.ini file and access to the SSH keys for the cluster; the scan is invoked using docker; the following script shows how we invoke this tool from of CI/CD pipeline:

Running cne_scan
set -x
 
TMP=/tmp/cne_scan\$\$
CLUSTER=${params.CLUSTER}
SSHCREDS=${params.BASTION_USER}@${params.BASTION_HOSTNAME}
REPOHOST=${params.REPO_HOSTNAME}:5000
 
# clean out any old logs before the new run
rm -rf logs/k8s* logs/scan
ssh \${SSHCREDS} sudo rm -rf /var/occne/cluster/\${CLUSTER}/k8s*
ssh \${SSHCREDS} sudo rm -rf /var/occne/cluster/\${CLUSTER}/scan
 
for img in aquasec/kube-bench:latest docker/docker-bench-security:latest ; do
 
    # Pull scan containers from public repo to central repo
    docker pull \${img}
    docker tag \${img} \${REPOHOST}/\${img}
    docker push \${REPOHOST}/\${img}
 
    # Pull containers from central repo to cluster repo
    ssh -t -t \${SSHCREDS} docker pull \${REPOHOST}/\${img}
    ssh -t -t \${SSHCREDS} docker tag \${REPOHOST}/\${img} '`hostname`:5000'/\${img}
    ssh -t -t \${SSHCREDS} docker push '`hostname`:5000'/\${img}
done
 
mkdir \${TMP}
 
# now fetch the latest oval rules
wget https://linux.oracle.com/security/oval/com.oracle.elsa-all.xml.bz2 -O \${TMP}/com.oracle.elsa-all.xml.bz2
 
# and push the rules to the bastion
scp \${TMP}/com.oracle.elsa-all.xml.bz2 \${SSHCREDS}:/var/occne/cluster/\${CLUSTER}/com.oracle.elsa-all.xml.bz2
 
# get rid of the tmp dir
rm -rf \${TMP}
 
# run the scans
ssh -t -t \${SSHCREDS} 'docker run -it --rm \
     --name DEPLOY_${params.CLUSTER}${BUILD_NUMBER} \
     --network host -v /var/occne/cluster/${params.CLUSTER}:/host \
     -v /var/occne/cluster/${params.CLUSTER}/com.oracle.elsa-all.xml.bz2:/tmp/com.oracle.elsa-all.xml.bz2 \
     -v /var/occne:/var/occne:rw -e OCCNEARGS=${params.CNE_SCAN_ARGS} ${params.CNE_SCAN_IMAGE} '
 
# copy back the results
rsync -a \${SSHCREDS}:/var/occne/cluster/\${CLUSTER}/k8s* logs || true
rsync -a \${SSHCREDS}:/var/occne/cluster/\${CLUSTER}/scan logs || true


OSSA Cipherscan

Cipherscan performs OSSA TLS cipher security compliance and vulnerability assessment for hosts.   We run this against our deployed OC-CNE environment as a part of our CI/CD self test processes.  

The OSSA Cipherscan tools performs the following actions against all nodes in the cluster:

  1. use nmap to identify all open ports on all hosts (returned as .../<host>/portscan.xml)
  2. use nmap to identify all open ports on all hosts that accept TLS;  for each host / TLS port:
    1. perform cipherscan to identify TLS ciphers accepted by the host/port
    2. categorize the cipher selections using OSSA criteria
    3. Return results as ../<host>/<port>/cipherscan.html

OpenSCAP/openscap

Open SCAP performs security compliance and vulnerability assessment for hosts.   We run this against our deployed OC-CNE environment as a part of our CI/CD self test processes.  We are currently installing openscap via YUM from the OL7 repository - this is provided by Red Hat.

docker/docker-bench-security

Docker Bench Security checks for dozens of common best-practices for deploying docker containers in production.  We run this against our deployed OC-CNE environment as a part of our CI/CD self test processes.

aquasecurity/kube-bench

Kube Bench checks CIS Kubernetes Benchmark conformance.  We plan to run this against our deployed OC-CNE environment as a part of our CI/CD self test processes.

Comments

Popular posts from this blog

Supporting OpenTracing jaeger in spring boot applications

This page describes code changes in a typical spring-boot based application to support OpenTracing and jaeger. Instrumenting a tracer If you are creating a simple spring boot application that uses  spring-boot-starter-web , by default, the application does not support writing traces to jaeger. To support jaeger tracing, the first thing is to modify the build.gradle to add dependency of jaeger: dependencies {      implementation  'org.springframework.boot:spring-boot-starter-web'      implementation  'io.opentracing.contrib:opentracing-spring-web-starter:3.0.1'      // support opentracing jaeger      implementation  'io.opentracing.contrib:opentracing-spring-jaeger-starter:3.1.2'      testImplementation( 'org.springframework.boot:spring-boot-starter-test' ) {          exclude group:  'org.junit.vintage' , module:  'junit...

Fortify Tooling User Guide

  Introduction The fortify-tools container is located within a shared repository in OCIR and requires a JWT to be able to access.  The variable WF_JWT will need to be set to a valid MAT You will want to choose one of three ways to use the Fortify SCA tooling: Integration Description Using the Fortify Tools Standalone to Scan a Project This is for using the tooling without integration in GitLab CI or Jenkins CI. Using the Fortify Tools In GitLab CI to Scan a Project This is for using the tooling against a project that whose code is hosted in GitLab and whose CI engine is GitLab CI. Using the Fortify Tools In Jenkins CI to Scan a Project This is for using the tooling against a project that whose code is hosted in GitLab and whose CI engine is Jenkins CI. Using the Fortify Tools Standalone to Scan a Project Simple Usage Run the Fortify Tools in a container docker run -t --rm -v <path to project source root directory>:/var/fortify/src phx.ocir.io/oraclegbudevcorp/cn-shared/s...