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,31 +3,41 @@ name: release
on:
push:
tags:
- 'release/*'
- "release/*"
jobs:
package:
runs-on: ubuntu-latest
container:
image: docker://rust:1.41.0-buster
steps:
- name: git co
uses: actions/checkout@v1
- name: meta
id: release-tag-meta
uses: ./.github/actions/release-tag-meta
with:
git-ref: ${{ github.ref }}
#- run: echo "${{ toJSON(steps.release-tag-meta) }}"
- name: make package
env:
CARGO_RELEASE: "1"
PACKAGE_VERSION: ${{ steps.release-tag-meta.outputs.name }}
run: make package
- name: release
uses: softprops/action-gh-release@b21b43d
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
name: ${{ steps.release-tag-meta.outputs.name }}
files: target/release/package/*
- name: git co
uses: actions/checkout@v1
- name: meta
id: release-tag-meta
uses: ./.github/actions/release-tag-meta
with:
git-ref: ${{ github.ref }}
#- run: echo "${{ toJSON(steps.release-tag-meta) }}"
- name: package
env:
CARGO_RELEASE: "1"
PACKAGE_VERSION: test-0.0.0 # ${{ steps.release-tag-meta.outputs.name }}
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
uses: softprops/action-gh-release@b21b43d
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
name: ${{ steps.release-tag-meta.outputs.name }}
files: target/release/package/*

View File

@ -14,6 +14,7 @@ TARGET_BIN = $(TARGET)/linkerd2-proxy
PKG_ROOT = $(TARGET)/package
PKG_NAME = linkerd2-proxy-$(PACKAGE_VERSION)
PKG_BASE = $(PKG_ROOT)/$(PKG_NAME)
PKG_CHECKSEC = $(PKG_BASE)-checksec.json
PKG = $(PKG_NAME).tar.gz
SHASUM = shasum -a 256
@ -40,7 +41,7 @@ endif
$(TARGET_BIN): fetch
$(CARGO_BUILD) -p linkerd2-proxy
$(PKG_ROOT)/$(PKG): $(TARGET_BIN)
$(PKG_ROOT)/$(PKG) $(PKG_CHECKSEC): $(TARGET_BIN)
mkdir -p $(PKG_BASE)/bin
cp LICENSE $(PKG_BASE)
cp $(TARGET_BIN) $(PKG_BASE)/bin/linkerd2-proxy
@ -51,6 +52,7 @@ ifdef CARGO_DEBUG
chmod 644 $(PKG_BASE)/linkerd2-proxy.obj ; \
fi
endif
./checksec.sh $(PKG_BASE)/bin/linkerd2-proxy >$(PKG_CHECKSEC) || true
cd $(PKG_ROOT) && \
tar -czvf $(PKG) $(PKG_NAME) && \
($(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
sed -i'' -Ee "s/rust:[0-9]+\.[0-9]+\.[0-9]+/rust:$VERSION/" Dockerfile
find .github -name \*.yml \
-exec sed -i'' -Ee "s|docker://rust:[0-9]+\.[0-9]+\.[0-9]+|docker://rust:$VERSION|" '{}' \;
find .github -name \*.yml -or -name Dockerfile\* \
-exec sed -i'' -Ee "s|rust:[0-9]+\.[0-9]+\.[0-9]+|rust:$VERSION|" '{}' \;