This document explains how the components-build-helper (CBH) builds docker images for Java and NodeJs components and how it checks for the vulnerabilities in the components.
The component vulnerability checks are the essential part of the component build process. There are two distinct parts to these checks:
CBH does this during the CI/CD component build process using the grype tool.CBH has a build_component_docker command, which builds docker image, checks
vulnerabilities using the grype tool and
pushes images to the Docker Hub. The build_component_docker accepts the
following environment variables:
DOCKER_USERNAME - username to authenticate in the docker registryDOCKER_PASSWORD - password to authenticate in the docker registryDRY_RUN - If true , disables pushing built docker image to the docker registryLimitation:
build_component_dockercommand supports pushing docker image only to the elasticio Docker Hub repository.
Example of component CI/CD configuration:
version: 2.1
parameters:
node-version:
type: string
default: "16.13.2"
orbs:
node: circleci/node@5.0.0
jobs:
build:
docker:
- image: cimg/base:stable
user: root
steps:
- checkout
- node/install:
node-version: << pipeline.parameters.node-version >>
- setup_remote_docker:
version: 19.03.13
docker_layer_caching: true
# build and push Docker image
- run:
name: Install component-build-helper lib
command: npm install -g @elastic.io/component-build-helper
- run:
name: Build and publish docker image
command: build_component_docker
workflows:
publish_release:
jobs:
- build:
name: "Build and publish docker image"
filters:
branches:
ignore: /.*/
tags:
only: /^([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+)?$/
GRYPE_DISABLED=true environment variable..grype-ignore.yaml file. Place this file at the root of the project. The file should have a root element ignore containing children elements following the grype ignore rules like:ignore:
- vulnerability: CVE-****-******
- vulnerability: CVE-****-******
- package:
type: apk
When you deploy a Java based component code, the CBH uses generateDockerfile
function to create Dockerfile with structure and commands (including ./gradlew build)
needed to build a docker image with a proper Java runtime environment.
The CBH supports the following LTS Java versions: 1.8 (Java SE 8), 11 (Java SE 11)
and 17 (Java SE 11). Depending on the version (or the targetCompatibility value),
CBH uses different build/runtime images as build and runtime environment to
address potential vulnerabilities in the runtime docker images:
| targetCompatibility | build images | runtime image |
|---|---|---|
| 1.8 | amazoncorretto:8-alpine-jdk | amazoncorretto:8-alpine-jre |
| 11 | amazoncorretto:11-alpine-jdk | alpine:latest |
| 17 | amazoncorretto:17-alpine-jdk | alpine:latest |
The CBH takes advantage of the Java module packaging mechanism to identify all necessary modules for the component class-path and creates a runtime image which contains only them.
You can change this behaviour by adding your list of runtime java models using jdeps.info file, which must be in the root directory of the component sources. This configuration file must have a comma separated list of modules you wish to include:
java.base,java.desktop,java.management,java.security.jgss,java.security.sasl,java.sql.rowset,jdk.security.auth,jdk.unsupported
The CBH supports only the Gradle to build Java based component code. Currently, it will use the gradle version 7.4.2 by default. If the component code requires a special version of gradle, you can configure the wrapper in the build.gradle file like:
wrapper {
gradleVersion = '5.4.1'
}
However, there are limitations and some unique configurations one should remember while configuring the build process for the Java components.
Limitation:
CBHsupports only the gradle wrapper generated using version above3.2. Starting from the gradle version3.2, it generates wrapper with#!/usr/bin/env shinstead of#!/usr/bin/env bash.CBHgenerates Dockerfile without bash installed.
CBH expects to find built classes in the build/classes/main directory. By default, gradle uses another directory to locate built classes. That’s why build.gradle file must contains right sourceSets configuration:
sourceSets {
main {
java.outputDir = file('build/classes/main')
}
test {
java.outputDir = file('build/classes/test')
}
integrationTest {
java.outputDir = file('build/classes/integrationtest')
java { srcDir file('src/integration-test/java') }
resources { srcDir file('src/integration-test/resources') }
}
}
As a component developer, you must configure the vulnerability checks in the build.gradle file and configure nightly jobs in the CI to identify new vulnerabilities in the used libraries.
You can configure the org.owasp.dependencycheck.gradle.DependencyCheckPlugin
to identify vulnerabilities. Configuration must contain a suppression file, where
you can exclude not relevant vulnerabilities. See https://plugins.gradle.org/plugin/org.owasp.dependencycheck for more details.
When you deploy a NodeJs based component code, the CBH detects the package.json
and package-lock.json files and starts the build process (npm install) for the
NodeJs based components. Next, the CBH uses generateDockerfile function to
create Dockerfile image and stores it the docker registry.
To guarantee a proper build and execution of NodeJs component, we recommend using
only the officially supported NodeJs versions.
NodeJs version range should contain the last patch of selected LTS node version
and have the following format: 16.x(last patch of node 16), 18.x(last patch
of node 18). CBH automatically finds the last patch version and specifies it on
the docker image tag. As a component developer, specify the version in the
package.json file in the engines.node part like:
"engines": {
"node": "18.x"
}
The CBH uses the latest node:${version}-alpine docker image as build and
runtime to help address the vulnerabilities in the runtime docker image.
You must configure a nightly job in the CI to identify new vulnerabilities in the used npm packages. We found that the better-npm-audit package provides flexibility and functionality to do the job. It has a functionality to add unnecessary vulnerabilities to the ignore file.
Sometimes the developer needs to install additional packages for the component
image. By default, CBH generates Dockerfile based on the alpine Linux,
with the following dependencies:
gittinipython3makeg*++*If you need to customise the Dockerfile image, you can create a custom Dockerfile by issuing the following command:
npm i @elastic.io/component-build-helper -g
component_cli generateDockerfile ./ > /your/component/root/dir/Dockerfile
This will create the following Dockerfile structure:
FROM node:14-alpine AS base
RUN apk add --update git tini python3 make g++ && rm -rf /var/cache/apk/*
WORKDIR /home/node
COPY --chown=node:node . ./
FROM base AS dependencies
ENV LOG_OUTPUT_MODE=short
USER node
RUN npm config set update-notifier false
RUN npm install --no-audit
RUN npm test
RUN npm prune --production
RUN rm -rf spec* .circleci README.md LICENSE .idea
FROM node:14-alpine AS release
LABEL elastic.io.component=""
LABEL elastic.io.logo=""
WORKDIR /home/node
COPY --from=base --chown=node:node /sbin/tini /sbin/tini
COPY --from=dependencies --chown=node:node /home/node /home/node
USER node
ENTRYPOINT ["/sbin/tini", "-v", "-e", "143", "--"]
base stage.release stage.In case you wand to use custom Dockerfile, omit elastic.io.component and
elastic.io.logo labels in your custom Dockerfile. The generateDockerfile
command will add these labels automatically during the deployment.