Add checksec to the release process (#476)

A recent [Twitter thread][mudge] suggested that tools like
[`checksec`][checksec] be used to validate release binaries. Checksec
reports whether modern security features like stack canaries are
employed. Proxy builds appear to do pretty well out-of-the-box.

This change introduces a checksec.sh wrapper that is used by the
Makefile during packaging. A new _package_ github action is introduced
to provide `checksec` and `jq` dependencies at runtime. (Note: the
version of checksec provided by debian does not include JSON output, so
it is instead fetched directly from GitHub).

During an automated release, the generated checksec is compared to an
expected set of values and, if a regression is detected, the release
will fail.

[mudge]: https://twitter.com/dotMudge/status/1249359519471341569
[checksec]: https://github.com/slimm609/checksec.sh
This commit is contained in:
Oliver Gould 2020-04-15 09:54:06 -07:00 committed by GitHub
parent 444174a194
commit f1a89ef953
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 111 additions and 26 deletions

10
.github/actions/package/Dockerfile vendored Normal file
View File

@ -0,0 +1,10 @@
ARG BASE_IMAGE=rust:1.41.0-buster
FROM $BASE_IMAGE
WORKDIR /linkerd
RUN apt-get update && \
apt-get install -y jq && \
apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/
# v2.1.0
ARG CHECKSEC_SHA=04582bad41589ad479ca8b1f0170ed317475b5a5
RUN (cd /usr/local/bin && curl -vsLO "https://github.com/slimm609/checksec.sh/blob/$CHECKSEC_SHA/checksec")
COPY expected-checksec.json validate-checksec.sh /linkerd/

View File

@ -0,0 +1,10 @@
{
"canary": "yes",
"fortify_source": "yes",
"nx": "yes",
"pie": "yes",
"relro": "full",
"rpath": "no",
"runpath": "no",
"symbols": "no"
}

25
.github/actions/package/validate-checksec.sh vendored Executable file
View File

@ -0,0 +1,25 @@
#!/bin/sh
set -eu
if [ $# -ne 2 ]; then
(
echo "usage: $0 EXPECTED RECEIVED"
echo
echo "Found $# args"
) >&2
exit 64
fi
expected_file="$1"
received_file="$2"
if [ ! -f "$expected_file" ]; then
echo "JSON output not found: $expected_file" >&2
exit 1
fi
if [ ! -f "$received_file" ]; then
echo "JSON output not found: $received_file" >&2
exit 1
fi
jq -S '.' "$received_file" | diff -u "$expected_file" - >&2

View File

@ -3,27 +3,37 @@ name: release
on: on:
push: push:
tags: tags:
- 'release/*' - "release/*"
jobs: jobs:
package: package:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container:
image: docker://rust:1.41.0-buster
steps: steps:
- name: git co - name: git co
uses: actions/checkout@v1 uses: actions/checkout@v1
- name: meta - name: meta
id: release-tag-meta id: release-tag-meta
uses: ./.github/actions/release-tag-meta uses: ./.github/actions/release-tag-meta
with: with:
git-ref: ${{ github.ref }} git-ref: ${{ github.ref }}
#- run: echo "${{ toJSON(steps.release-tag-meta) }}" #- run: echo "${{ toJSON(steps.release-tag-meta) }}"
- name: make package
- name: package
env: env:
CARGO_RELEASE: "1" CARGO_RELEASE: "1"
PACKAGE_VERSION: ${{ steps.release-tag-meta.outputs.name }} PACKAGE_VERSION: test-0.0.0 # ${{ steps.release-tag-meta.outputs.name }}
run: make package uses: ./.github/actions/package
with:
entrypoint: make
args: package
- name: checksec
uses: ./.github/actions/package
with:
entrypoint: /linkerd/validate-checksec.sh
args: /linkerd/expected-checksec.json "target/release/package/linkerd2-proxy-${{ steps.release-tag-meta.outputs.name }}-checksec.json"
- name: release - name: release
uses: softprops/action-gh-release@b21b43d uses: softprops/action-gh-release@b21b43d
env: env:

View File

@ -14,6 +14,7 @@ TARGET_BIN = $(TARGET)/linkerd2-proxy
PKG_ROOT = $(TARGET)/package PKG_ROOT = $(TARGET)/package
PKG_NAME = linkerd2-proxy-$(PACKAGE_VERSION) PKG_NAME = linkerd2-proxy-$(PACKAGE_VERSION)
PKG_BASE = $(PKG_ROOT)/$(PKG_NAME) PKG_BASE = $(PKG_ROOT)/$(PKG_NAME)
PKG_CHECKSEC = $(PKG_BASE)-checksec.json
PKG = $(PKG_NAME).tar.gz PKG = $(PKG_NAME).tar.gz
SHASUM = shasum -a 256 SHASUM = shasum -a 256
@ -40,7 +41,7 @@ endif
$(TARGET_BIN): fetch $(TARGET_BIN): fetch
$(CARGO_BUILD) -p linkerd2-proxy $(CARGO_BUILD) -p linkerd2-proxy
$(PKG_ROOT)/$(PKG): $(TARGET_BIN) $(PKG_ROOT)/$(PKG) $(PKG_CHECKSEC): $(TARGET_BIN)
mkdir -p $(PKG_BASE)/bin mkdir -p $(PKG_BASE)/bin
cp LICENSE $(PKG_BASE) cp LICENSE $(PKG_BASE)
cp $(TARGET_BIN) $(PKG_BASE)/bin/linkerd2-proxy cp $(TARGET_BIN) $(PKG_BASE)/bin/linkerd2-proxy
@ -51,6 +52,7 @@ ifdef CARGO_DEBUG
chmod 644 $(PKG_BASE)/linkerd2-proxy.obj ; \ chmod 644 $(PKG_BASE)/linkerd2-proxy.obj ; \
fi fi
endif endif
./checksec.sh $(PKG_BASE)/bin/linkerd2-proxy >$(PKG_CHECKSEC) || true
cd $(PKG_ROOT) && \ cd $(PKG_ROOT) && \
tar -czvf $(PKG) $(PKG_NAME) && \ tar -czvf $(PKG) $(PKG_NAME) && \
($(SHASUM) $(PKG) >$(PKG_NAME).txt) && \ ($(SHASUM) $(PKG) >$(PKG_NAME).txt) && \

28
checksec.sh Executable file
View File

@ -0,0 +1,28 @@
#!/bin/bash
set -eu
require_from() {
if ! command -v "$1" >/dev/null 2>/dev/null ; then
echo "Please acquire $1 from $2" >&2
exit 1
fi
}
require_from readelf binutils
require_from checksec https://github.com/slimm609/checksec.sh
require_from jq https://stedolan.github.io/jq/
if [ $# -ne 1 ]; then
echo "usage: $0 EXECUTABLE" >&2
exit 64
fi
path="$1"
if [ ! -x "$path" ]; then
echo "Executable not found: $path" >&2
exit 1
fi
out=$(checksec --output=json --file="$path")
echo "$out" | jq ".[\"$path\"] | del(.\"fortify-able\") | del(.fortified)"

View File

@ -16,5 +16,5 @@ fi
echo "$VERSION" > rust-toolchain echo "$VERSION" > rust-toolchain
sed -i'' -Ee "s/rust:[0-9]+\.[0-9]+\.[0-9]+/rust:$VERSION/" Dockerfile sed -i'' -Ee "s/rust:[0-9]+\.[0-9]+\.[0-9]+/rust:$VERSION/" Dockerfile
find .github -name \*.yml \ find .github -name \*.yml -or -name Dockerfile\* \
-exec sed -i'' -Ee "s|docker://rust:[0-9]+\.[0-9]+\.[0-9]+|docker://rust:$VERSION|" '{}' \; -exec sed -i'' -Ee "s|rust:[0-9]+\.[0-9]+\.[0-9]+|rust:$VERSION|" '{}' \;