From b1871b0f2e055d38c5e25cdcb040a9acf1eaf1f6 Mon Sep 17 00:00:00 2001 From: Caleb Doxsey Date: Thu, 18 Feb 2021 15:22:46 -0700 Subject: [PATCH] envoy: validate binary checksum (#1908) * envoy: validate binary checksum * address comments * change to info * fix order --- Makefile | 6 +++-- go.mod | 1 + go.sum | 8 ++++++ internal/envoy/envoy.go | 32 ++++++++++++++++++++++++ scripts/embed-envoy.bash | 32 +++--------------------- scripts/get-envoy.bash | 54 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 102 insertions(+), 31 deletions(-) create mode 100755 scripts/get-envoy.bash diff --git a/Makefile b/Makefile index 1b755c13f..77e48a539 100644 --- a/Makefile +++ b/Makefile @@ -73,13 +73,15 @@ frontend: ## Runs go generate on the static assets package. .PHONY: build build: ## Builds dynamic executables and/or packages. @echo "==> $@" - @CGO_ENABLED=0 GO111MODULE=on $(GO) build -tags "$(BUILDTAGS)" ${GO_LDFLAGS} -o $(BINDIR)/$(NAME) ./cmd/"$(NAME)" + ./scripts/get-envoy.bash + @CGO_ENABLED=0 GO111MODULE=on $(GO) build -tags "$(BUILDTAGS)" ${GO_LDFLAGS} -ldflags="-X github.com/pomerium/pomerium/internal/envoy.Checksum=$$(cat ./bin/envoy.sha256 | tr -d '\n')" -o $(BINDIR)/$(NAME) ./cmd/"$(NAME)" ./scripts/embed-envoy.bash $(BINDIR)/$(NAME) .PHONY: build-debug build-debug: ## Builds binaries appropriate for debugging @echo "==> $@" - @CGO_ENABLED=0 GO111MODULE=on $(GO) build -gcflags="all=-N -l" -o $(BINDIR)/$(NAME) ./cmd/"$(NAME)" + ./scripts/get-envoy.bash + @CGO_ENABLED=0 GO111MODULE=on $(GO) build -gcflags="all=-N -l" -ldflags="-X github.com/pomerium/pomerium/internal/envoy.Checksum=$$(cat ./bin/envoy.sha256 | tr -d '\n')" -o $(BINDIR)/$(NAME) ./cmd/"$(NAME)" ./scripts/embed-envoy.bash $(BINDIR)/$(NAME) diff --git a/go.mod b/go.mod index fe2cccecd..82a2ffdcc 100644 --- a/go.mod +++ b/go.mod @@ -30,6 +30,7 @@ require ( github.com/hashicorp/go-multierror v1.1.0 github.com/hashicorp/golang-lru v0.5.4 github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect + github.com/lib/pq v1.1.1 // indirect github.com/lithammer/shortuuid/v3 v3.0.5 github.com/martinlindhe/base36 v1.1.0 github.com/mitchellh/hashstructure/v2 v2.0.1 diff --git a/go.sum b/go.sum index 86b1db4c3..f6379aefc 100644 --- a/go.sum +++ b/go.sum @@ -140,8 +140,10 @@ github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w= @@ -365,6 +367,8 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lib/pq v0.0.0-20180327071824-d34b9ff171c2 h1:hRGSmZu7j271trc9sneMrpOW7GN5ngLm8YUZIPzf394= github.com/lib/pq v0.0.0-20180327071824-d34b9ff171c2/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4= +github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/libdns/libdns v0.1.0 h1:0ctCOrVJsVzj53mop1angHp/pE3hmAhP7KiHvR0HD04= github.com/libdns/libdns v0.1.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= @@ -393,6 +397,7 @@ github.com/miekg/dns v1.1.30 h1:Qww6FseFn8PRfw07jueqIXqodm0JKiiKuK0DeXSqfyo= github.com/miekg/dns v1.1.30/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= @@ -536,13 +541,16 @@ github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.20.0 h1:38k9hgtUBdxFwE34yS8rTHmHBa4eN16E4DJlv177LNs= github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo= +github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= diff --git a/internal/envoy/envoy.go b/internal/envoy/envoy.go index 25192c2c0..07c140d2b 100644 --- a/internal/envoy/envoy.go +++ b/internal/envoy/envoy.go @@ -4,9 +4,12 @@ package envoy import ( "bufio" "bytes" + "crypto/sha256" + "encoding/hex" "errors" "fmt" "io" + "io/ioutil" "net" "os" "os/exec" @@ -43,6 +46,9 @@ const ( configFileName = "envoy-config.yaml" ) +// Checksum is the embedded envoy binary checksum. This value is populated by `make build`. +var Checksum string + type serverOptions struct { services string logLevel string @@ -76,6 +82,27 @@ func NewServer(src config.Source, grpcPort, httpPort string) (*Server, error) { envoyPath = "envoy" } + fullEnvoyPath, err := exec.LookPath(envoyPath) + if err != nil { + return nil, fmt.Errorf("no envoy binary found: %w", err) + } + + // Checksum is written at build time, if it's not empty we verify the binary + if Checksum != "" { + bs, err := ioutil.ReadFile(fullEnvoyPath) + if err != nil { + return nil, fmt.Errorf("error reading envoy binary for checksum verification: %w", err) + } + h := sha256.New() + h.Write(bs) + s := hex.EncodeToString(h.Sum(nil)) + if Checksum != s { + return nil, fmt.Errorf("invalid envoy binary, expected %s but got %s", Checksum, s) + } + } else { + log.Info().Msg("no checksum defined, envoy binary will not be verified!") + } + srv := &Server{ wd: wd, grpcPort: grpcPort, @@ -86,6 +113,11 @@ func NewServer(src config.Source, grpcPort, httpPort string) (*Server, error) { src.OnConfigChange(srv.onConfigChange) srv.onConfigChange(src.GetConfig()) + log.Info(). + Str("path", envoyPath). + Str("checksum", Checksum). + Msg("running envoy") + return srv, nil } diff --git a/scripts/embed-envoy.bash b/scripts/embed-envoy.bash index 4c8296bae..6485bf3cd 100755 --- a/scripts/embed-envoy.bash +++ b/scripts/embed-envoy.bash @@ -2,42 +2,16 @@ set -euo pipefail BINARY=$1 - -ENVOY_VERSION=1.16.2 DIR=$(dirname "${BINARY}") -TARGET="${TARGET:-"$(go env GOOS)_$(go env GOARCH)"}" - -if [[ "${TARGET}" == darwin_* ]]; then - ENVOY_PLATFORM="darwin" -elif [[ "${TARGET}" == linux_* ]]; then - ENVOY_PLATFORM="linux_glibc" -else - echo "unsupported TARGET: ${TARGET}" - exit 1 -fi - -## TODO we should be able to replace this with a utility that consumes -## https://godoc.org/github.com/tetratelabs/getenvoy/pkg/binary/envoy -## https://golang.org/pkg/archive/zip/#Writer.SetOffset -export PATH=$PATH:$(go env GOPATH)/bin -if [ "$TARGET" == "linux_arm64" ]; then - ENVOY_PATH="$DIR/$TARGET" - mkdir -p "$ENVOY_PATH" - curl -L -o "$ENVOY_PATH/envoy" https://github.com/pomerium/envoy-binaries/releases/download/v${ENVOY_VERSION}/envoy-linux-arm64 -else - env HOME="${DIR}" getenvoy fetch standard:${ENVOY_VERSION}/${ENVOY_PLATFORM} - ENVOY_PATH=${DIR}/.getenvoy/builds/standard/${ENVOY_VERSION}/${ENVOY_PLATFORM}/bin -fi -ARCHIVE="${ENVOY_PATH}/envoy.zip" ( - cd "${ENVOY_PATH}" + cd "$DIR" zip envoy.zip envoy ) -echo "appending ${ARCHIVE} to ${BINARY}" +echo "appending $DIR/envoy.zip to ${BINARY}" if [ "$(unzip -z -qq "$BINARY" 2>&1)" != "" ]; then - cat "${ARCHIVE}" >>"${BINARY}" + cat "$DIR/envoy.zip" >>"${BINARY}" fi zip -A "${BINARY}" diff --git a/scripts/get-envoy.bash b/scripts/get-envoy.bash new file mode 100755 index 000000000..c39d8a8b2 --- /dev/null +++ b/scripts/get-envoy.bash @@ -0,0 +1,54 @@ +#!/bin/bash +set -euo pipefail + +PATH="$PATH:$(go env GOPATH)/bin" +export PATH + +_envoy_version=1.16.2 +_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)/../bin" +_target="${TARGET:-"$(go env GOOS)_$(go env GOARCH)"}" + +if [[ "${_target}" == darwin_* ]]; then + _envoy_platform="darwin" +elif [[ "${_target}" == linux_* ]]; then + _envoy_platform="linux_glibc" +else + echo "unsupported TARGET: ${_target}" + exit 1 +fi + +is_command() { + command -v "$1" >/dev/null +} + +hash_sha256() { + TARGET=${1:-/dev/stdin} + if is_command gsha256sum; then + hash=$(gsha256sum "$TARGET") || return 1 + echo "$hash" | cut -d ' ' -f 1 + elif is_command sha256sum; then + hash=$(sha256sum "$TARGET") || return 1 + echo "$hash" | cut -d ' ' -f 1 + elif is_command shasum; then + hash=$(shasum -a 256 "$TARGET" 2>/dev/null) || return 1 + echo "$hash" | cut -d ' ' -f 1 + elif is_command openssl; then + hash=$(openssl -dst openssl dgst -sha256 "$TARGET") || return 1 + echo "$hash" | cut -d ' ' -f a + else + echo "hash_sha256 unable to find command to compute sha-256 hash" + return 1 + fi +} + +mkdir -p "$_dir" + +if [ "$_target" == "linux_arm64" ]; then + mkdir -p "$_dir" + curl -L -o "$_dir/envoy" https://github.com/pomerium/envoy-binaries/releases/download/v${_envoy_version}/envoy-linux-arm64 +else + env HOME="$_dir" getenvoy fetch standard:${_envoy_version}/${_envoy_platform} + cp -f "$_dir/.getenvoy/builds/standard/${_envoy_version}/${_envoy_platform}/bin/envoy" "$_dir/envoy" +fi + +hash_sha256 "$_dir/envoy" >"$_dir/envoy.sha256"