Skip to content

feat: add iron bank Dockerfile & manifest #5934

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Feb 7, 2023
Merged
42 changes: 41 additions & 1 deletion .github/workflows/security.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ jobs:
restore-keys: |
js-${{ runner.os }}-

- name: Install yq
run: go run github.com/mikefarah/yq/v4@v4.30.6

- name: Build Coder linux amd64 Docker image
id: build
run: |
Expand All @@ -112,6 +115,17 @@ jobs:
make -j "$image_job"
echo "image=$(cat "$image_job")" >> $GITHUB_OUTPUT

- name: Build Coder linux amd64 Docker image (ironbank)
id: build-ironbank
run: |
set -euo pipefail
# NOTE: This is not a real image tag we publish.
image_tag="${{ steps.build.outputs.image }}-ironbank"
./scripts/ironbank/build_ironbank.sh \
--target "$image_tag" \
"build/coder_$(./scripts/version.sh)_linux_amd64"
echo "image=$image_tag" >> $GITHUB_OUTPUT

- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@9ab158e8597f3b310480b9a69402b419bc03dbd5
with:
Expand All @@ -124,10 +138,36 @@ jobs:
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: trivy-results.sarif
category: "Trivy"

- name: Run Trivy vulnerability scanner (ironbank)
uses: aquasecurity/trivy-action@7b7aa264d83dc58691451798b4d117d53d21edfe
with:
image-ref: ${{ steps.build-ironbank.outputs.image }}
format: sarif
output: trivy-results-ironbank.sarif
severity: "CRITICAL,HIGH"

# Update the tool name field in the ironbank SARIF file so it's not
# indistinguishable from findings in the non-ironbank SARIF file in the
# GitHub UI. Without this, findings from both scans would show up as
# "Trivy".
- name: Update tool name in SARIF file (ironbank)
run: |
set -euo pipefail
yq eval -i '.runs[0].tool.driver.name = "Trivy Ironbank"' trivy-results-ironbank.sarif

- name: Upload Trivy scan results to GitHub Security tab (ironbank)
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: trivy-results-ironbank.sarif
category: "Trivy Ironbank"

- name: Upload Trivy scan results as an artifact
uses: actions/upload-artifact@v2
with:
name: trivy
path: trivy-results.sarif
path: |
trivy-results.sarif
trivy-results-ironbank.sarif
retention-days: 7
6 changes: 6 additions & 0 deletions scripts/ironbank/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
coder.tar.gz
terraform.zip
terraform-provider-coder.zip

.terraform.zip.*
.terraform-provider-coder.zip.*
95 changes: 95 additions & 0 deletions scripts/ironbank/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
ARG BASE_REGISTRY=registry1.dso.mil
ARG BASE_IMAGE=ironbank/redhat/ubi/ubi8-minimal
ARG BASE_TAG=8.7

FROM ${BASE_REGISTRY}/${BASE_IMAGE}:${BASE_TAG}

SHELL ["/bin/bash", "-c"]

ENV LANG=en_US.UTF-8

RUN microdnf update --assumeyes && \
microdnf install --assumeyes \
ca-certificates \
git \
gzip \
shadow-utils \
tar \
unzip && \
microdnf clean all

# Configure the cryptography policy manually. These policies likely
# have no impact, since Go doesn't link against these libraries.
#
# Normally, one uses the update-crypto-policies script to create these
# links, which is included in the crypto-policies-scripts package, but
# that pulls in Python, so we create the links manually here. This
# list of links comes from running strace on the update-crypto-policies
# script (strace update-crypto-policies --set FIPS) in Fedora, since
# RHEL and UBI do not provide an strace package by default.
RUN echo "FIPS" >/etc/crypto-policies/config && \
cp --force /usr/share/crypto-policies/policies/FIPS.pol /etc/crypto-policies/state/CURRENT.pol && \
echo "FIPS" >/etc/crypto-policies/state/current && \
ln --symbolic --force /usr/share/crypto-policies/FIPS/bind.txt /etc/crypto-policies/back-ends/bind.config && \
ln --symbolic --force /usr/share/crypto-policies/FIPS/gnutls.txt /etc/crypto-policies/back-ends/gnutls.config && \
ln --symbolic --force /usr/share/crypto-policies/FIPS/java.txt /etc/crypto-policies/back-ends/java.config && \
ln --symbolic --force /usr/share/crypto-policies/FIPS/krb5.txt /etc/crypto-policies/back-ends/krb5.config && \
ln --symbolic --force /usr/share/crypto-policies/FIPS/libreswan.txt /etc/crypto-policies/back-ends/libreswan.config && \
ln --symbolic --force /usr/share/crypto-policies/FIPS/libssh.txt /etc/crypto-policies/back-ends/libssh.config && \
ln --symbolic --force /usr/share/crypto-policies/FIPS/nss.txt /etc/crypto-policies/back-ends/nss.config && \
ln --symbolic --force /usr/share/crypto-policies/FIPS/openssh.txt /etc/crypto-policies/back-ends/openssh.config && \
ln --symbolic --force /usr/share/crypto-policies/FIPS/opensshserver.txt /etc/crypto-policies/back-ends/opensshserver.config && \
ln --symbolic --force /usr/share/crypto-policies/FIPS/openssl.txt /etc/crypto-policies/back-ends/openssl.config && \
ln --symbolic --force /usr/share/crypto-policies/FIPS/opensslcnf.txt /etc/crypto-policies/back-ends/opensslcnf.config

# Copy and extract Coder binary from tar file. We have to put this in /opt to
# match the Dockerfile.
ARG CODER_BIN=/opt/coder
ARG CODER_BIN_TAR_GZ=coder.tar.gz
COPY "$CODER_BIN_TAR_GZ" /tmp/coder.tar.gz
RUN mkdir -p /opt && \
tar -xzvf /tmp/coder.tar.gz --directory /opt --strip-components=1 ./coder && \
rm /tmp/coder.tar.gz
ENV PATH="/opt:${PATH}"

# Copy and extract Terraform binary from zip file.
ARG TERRAFORM_BIN_DIR=/opt/terraform
ARG TERRAFORM_BIN_ZIP=terraform.zip
COPY "$TERRAFORM_BIN_ZIP" /tmp/terraform.zip
RUN mkdir -p "$TERRAFORM_BIN_DIR" && \
unzip /tmp/terraform.zip -d "$TERRAFORM_BIN_DIR" && \
rm /tmp/terraform.zip
ENV PATH="${TERRAFORM_BIN_DIR}:${PATH}"

# Install the Coder Terraform provider to a well-known location.
ARG TERRAFORM_PLUGINS_DIR=/opt/terraform/plugins
ARG TERRAFORM_CODER_PROVIDER_VERSION
ARG TERRAFORM_CODER_PROVIDER_ZIP=terraform-provider-coder.zip
COPY "$TERRAFORM_CODER_PROVIDER_ZIP" "${TERRAFORM_PLUGINS_DIR}/registry.terraform.io/coder/coder/terraform-provider-coder_${TERRAFORM_CODER_PROVIDER_VERSION}_linux_amd64.zip"

# Configure Terraform to use plugins from this dir.
COPY terraform-filesystem-mirror.tfrc /opt/terraform/config.tfrc
ENV TF_CLI_CONFIG_FILE=/opt/terraform/config.tfrc

# Uninstall the build dependencies.
RUN microdnf remove --assumeyes \
tar \
unzip && \
microdnf clean all

# Transfer ownership of the binaries to the 'coder' user.
RUN useradd coder \
--create-home \
--shell=/bin/bash \
--uid=1000 \
--user-group && \
chown --recursive --quiet coder:coder "$CODER_BIN" && \
chown --recursive --quiet coder:coder "$TERRAFORM_BIN_DIR" && \
chown --recursive --quiet coder:coder "$TERRAFORM_PLUGINS_DIR" && \
chmod 0755 /home/coder

USER 1000
ENV HOME /home/coder
ENV USER=coder

ENTRYPOINT [ "/opt/coder", "server" ]
106 changes: 106 additions & 0 deletions scripts/ironbank/build_ironbank.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#!/usr/bin/env bash

# This script builds the ironbank Docker image of Coder containing the given
# binary. Other dependencies will be automatically downloaded and cached.
#
# Usage: ./build_ironbank.sh --target image_tag path/to/coder

set -euo pipefail
# shellcheck source=scripts/lib.sh
source "$(dirname "${BASH_SOURCE[0]}")/../lib.sh"

image_tag=""

args="$(getopt -o "" -l target: -- "$@")"
eval set -- "$args"
while true; do
case "$1" in
--target)
image_tag="$2"
shift 2
;;
--)
shift
break
;;
*)
error "Unrecognized option: $1"
;;
esac
done

if [[ "$image_tag" == "" ]]; then
error "The --image-tag parameter is required"
fi

# Check dependencies
dependencies docker sha256sum yq
if [[ $(yq --version) != *" v4."* ]]; then
error "yq version 4 is required"
fi

if [[ "$#" != 1 ]]; then
error "Exactly one argument must be provided to this script, $# were supplied"
fi
if [[ ! -f "$1" ]]; then
error "File '$1' does not exist or is not a regular file"
fi
input_file="$(realpath "$1")"

# Make temporary dir for Docker build context.
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
pushd "$(dirname "${BASH_SOURCE[0]}")"
cp Dockerfile "$tmpdir/"
cp terraform-filesystem-mirror.tfrc "$tmpdir/"
popd

# Create a coder.tar.gz file.
execrelative ../archive.sh \
--format tar.gz \
--os linux \
--output "$tmpdir/coder.tar.gz" \
"$input_file"

# Download all resources in the hardening_manifest.yaml file except for
# coder.tar.gz (which we will make ourselves).
manifest_path="$(dirname "${BASH_SOURCE[0]}")/hardening_manifest.yaml"
resources="$(yq e '.resources[] | select(.filename != "coder.tar.gz") | [.filename, .url, .validation.value] | @tsv' "$manifest_path")"
while read -r line; do
filename="$(echo "$line" | cut -f1)"
url="$(echo "$line" | cut -f2)"
sha256_hash="$(echo "$line" | cut -f3)"

pushd "$(dirname "${BASH_SOURCE[0]}")"
target=".${filename}.${sha256_hash}"
if [[ ! -f "$target" ]]; then
log "Downloading $filename"
curl -sSL "$url" -o "$target"
fi

sum="$(sha256sum "$target" | cut -d' ' -f1)"
if [[ "$sum" != "$sha256_hash" ]]; then
rm "$target"
error "Downloaded $filename has hash $sum, but expected $sha256_hash"
fi
cp "$target" "$tmpdir/$filename"
popd
done <<<"$resources"

terraform_coder_provider_version="$(yq e '.args.TERRAFORM_CODER_PROVIDER_VERSION' "$manifest_path")"
if [[ "$terraform_coder_provider_version" == "" ]]; then
error "TERRAFORM_CODER_PROVIDER_VERSION not found in hardening_manifest.yaml"
fi

# Build the image.
pushd "$tmpdir"
docker build \
--build-arg BASE_REGISTRY=registry.access.redhat.com \
--build-arg BASE_IMAGE=ubi8/ubi-minimal \
--build-arg BASE_TAG=8.7 \
--build-arg TERRAFORM_CODER_PROVIDER_VERSION="$terraform_coder_provider_version" \
-t "$image_tag" \
. >&2
popd

echo "$image_tag"
65 changes: 65 additions & 0 deletions scripts/ironbank/hardening_manifest.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
apiVersion: v1

# The repository name in registry1, excluding /ironbank/
name: "coder/coder-enterprise/coder-service-2"

# List of tags to push for the repository in registry1
# The most specific version should be the first tag and will be shown
# on ironbank.dso.mil
tags:
- "0.15.3"
- "latest"

# Build args passed to Dockerfile ARGs
args:
# Needs to be kept in sync with the resource below.
TERRAFORM_CODER_PROVIDER_VERSION: "0.6.10"

# Docker image labels
labels:
org.opencontainers.image.title: "coder-service-v2"
# Human-readable description of the software packaged in the image
org.opencontainers.image.description: "Coder server binary, includes REST API, Terraform, and dashboard"
# License(s) under which contained software is distributed
org.opencontainers.image.licenses: "AGPL"
# URL to find more information on the image
org.opencontainers.image.url: "https://coder.com/docs"
# Name of the distributing entity, organization or individual
org.opencontainers.image.vendor: "Coder Technologies"
org.opencontainers.image.version: "0.15.3"
# Keywords to help with search (ex. "cicd,gitops,golang")
mil.dso.ironbank.image.keywords: "remote, workspaces"

# List of resources to make available to the offline build context
resources:
# Coder binary
- url: "https://github.com/coder/coder/releases/download/v0.15.3/coder_0.15.3_linux_amd64.tar.gz"
filename: "coder.tar.gz"
validation:
type: sha256
value: 2c88555777f1d9cc77a8f049093f4002472dc43d52b026e6784ef477bdced4a2
# Terraform binary, bundled inside of Coder to support air-gapped installs.
- url: https://releases.hashicorp.com/terraform/1.3.7/terraform_1.3.7_linux_amd64.zip
filename: "terraform.zip"
validation:
type: sha256
value: b8cf184dee15dfa89713fe56085313ab23db22e17284a9a27c0999c67ce3021e
# Coder Terraform provider, bundled inside of Coder to support air-gapped
# installs.
#
# The version of this provider needs to be kept in sync with the
# TERRAFORM_CODER_PROVIDER_VERSION build arg.
- url: https://github.com/coder/terraform-provider-coder/releases/download/v0.6.10/terraform-provider-coder_0.6.10_linux_amd64.zip
filename: "terraform-provider-coder.zip"
validation:
type: sha256
value: 4c2a16010621e146251f6fb5e27105dde9213d85ca8f3c8866c3f5a4159b81b0

# List of project maintainers
maintainers:
- email: "eric@coder.com"
name: "Eric Paulsen"
username: "ericpaulsen"
- email: "dean@coder.com"
name: "Dean Sheather"
username: "cdrdean"
5 changes: 5 additions & 0 deletions scripts/ironbank/terraform-filesystem-mirror.tfrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
provider_installation {
filesystem_mirror {
path = "/opt/terraform/plugins"
}
}
2 changes: 1 addition & 1 deletion scripts/lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ realpath() {
}

# We have to define realpath before these otherwise it fails on Mac's bash.
SCRIPT_DIR="$(realpath "$(dirname "${BASH_SOURCE[0]}")")"
SCRIPT_DIR="$(realpath "$(dirname "${BASH_SOURCE[1]}")")"
PROJECT_ROOT="$(cd "$SCRIPT_DIR" && realpath "$(git rev-parse --show-toplevel)")"

# pushd is a silent alternative to the real pushd shell command.
Expand Down