From adea7b5e20539edc30bb0b328365497e1d95bb44 Mon Sep 17 00:00:00 2001 From: Daniel Upton Date: Fri, 23 Sep 2022 11:18:30 +0100 Subject: [PATCH 1/6] Make our container image "distroless" Switches our release base image to harden our security posture. See here: https://github.com/GoogleContainerTools/distroless --- Dockerfile | 40 ++++++++++++++-------------------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/Dockerfile b/Dockerfile index 23caf8a6..ef60e0ef 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,23 +11,22 @@ FROM envoyproxy/envoy:v1.23.1 as envoy-binary # go-discover builds the discover binary (which we don't currently publish # either). FROM golang:1.19.1-alpine as go-discover -RUN go install github.com/hashicorp/go-discover/cmd/discover@49f60c093101c9c5f6b04d5b1c80164251a761a6 +RUN CGO_ENABLED=0 go install github.com/hashicorp/go-discover/cmd/discover@49f60c093101c9c5f6b04d5b1c80164251a761a6 -# =================================== -# -# Release images. -# -# =================================== +# Pull in dumb-init from alpine, as our distroless release image doesn't have a +# package manager and there's no RPM package for UBI. +FROM alpine:latest AS dumb-init +RUN apk add dumb-init # release-default release image # ----------------------------------- -FROM alpine:3.16 AS release-default +FROM gcr.io/distroless/base-debian11 AS release-default ARG BIN_NAME -ENV BIN_NAME=$BIN_NAME ARG PRODUCT_VERSION ARG PRODUCT_REVISION ARG PRODUCT_NAME=$BIN_NAME + # TARGETARCH and TARGETOS are set automatically when --platform is provided. ARG TARGETOS TARGETARCH @@ -40,17 +39,14 @@ LABEL name=${BIN_NAME}\ summary="Consul dataplane manages the proxy that runs within the data plane layer of Consul Service Mesh." \ description="Consul dataplane manages the proxy that runs within the data plane layer of Consul Service Mesh." -# Create a non-root user to run the software. -RUN addgroup $PRODUCT_NAME && \ - adduser -S -G $PRODUCT_NAME 100 - -COPY dist/$TARGETOS/$TARGETARCH/$BIN_NAME /usr/local/bin/ COPY --from=go-discover /go/bin/discover /usr/local/bin/ -COPY --from=envoy-binary /usr/local/bin/envoy /usr/local/bin/envoy -RUN apk add gcompat dumb-init +COPY --from=envoy-binary /usr/local/bin/envoy /usr/local/bin/ +COPY --from=dumb-init /usr/bin/dumb-init /usr/local/bin/ +COPY dist/$TARGETOS/$TARGETARCH/$BIN_NAME /usr/local/bin/ USER 100 -ENTRYPOINT ["/usr/bin/dumb-init", "/usr/local/bin/consul-dataplane"] + +ENTRYPOINT ["/usr/local/bin/dumb-init", "/usr/local/bin/consul-dataplane"] # Red Hat UBI-based image # This image is based on the Red Hat UBI base image, and has the necessary @@ -75,16 +71,7 @@ LABEL name=${BIN_NAME}\ summary="Consul dataplane connects an application to a Consul service mesh." \ description="Consul dataplane connects an application to a Consul service mesh." -RUN microdnf install -y wget shadow-utils - -# dumb-init is downloaded directly from GitHub because there's no RPM package. -# Its shasum is hardcoded. If you upgrade the dumb-init verion you'll need to -# also update the shasum. -RUN set -eux && \ - wget -O /usr/local/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.2.5/dumb-init_1.2.5_x86_64 && \ - echo 'e874b55f3279ca41415d290c512a7ba9d08f98041b28ae7c2acb19a545f1c4df /usr/local/bin/dumb-init' > dumb-init-shasum && \ - sha256sum --check dumb-init-shasum && \ - chmod +x /usr/local/bin/dumb-init +RUN microdnf install -y shadow-utils # Create a non-root user to run the software. RUN groupadd --gid 1000 $PRODUCT_NAME && \ @@ -94,6 +81,7 @@ RUN groupadd --gid 1000 $PRODUCT_NAME && \ COPY dist/$TARGETOS/$TARGETARCH/$BIN_NAME /usr/local/bin/ COPY --from=go-discover /go/bin/discover /usr/local/bin/ COPY --from=envoy-binary /usr/local/bin/envoy /usr/local/bin/envoy +COPY --from=dumb-init /usr/bin/dumb-init /usr/local/bin/ COPY LICENSE /licenses/copyright.txt USER 100 From 04e0cb59f5ba0226681644fcc900757c1a8ff5fb Mon Sep 17 00:00:00 2001 From: Daniel Upton Date: Mon, 17 Oct 2022 14:37:00 +0100 Subject: [PATCH 2/6] config: make it possible to read service ID from file --- README.md | 1 + cmd/consul-dataplane/main.go | 29 ++++++++++++++++++++++++----- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 988b3030..a7608a06 100644 --- a/README.md +++ b/README.md @@ -205,6 +205,7 @@ The `consul-dataplane` binary supports the following flags. | `-login-namespace` | string | | The Consul Enterprise namespace containing the auth method. | | `-login-partition` | string | | The Consul Enterprise partition containing the auth method. | | `-proxy-service-id` | string | | The proxy service instance's ID. | +| `-proxy-service-id-path` | string | | The path to a file containing the proxy service instance's ID. | | `-server-watch-disabled` | bool | `false` | Setting this prevents consul-dataplane from consuming the server update stream. This is useful for situations where Consul servers are behind a load balancer. | | `-service-namespace` | string | | The Consul Enterprise namespace in which the proxy service instance is registered. | | `-service-node-id` | string | | The ID of the Consul node to which the proxy service instance is registered. | diff --git a/cmd/consul-dataplane/main.go b/cmd/consul-dataplane/main.go index 9f074ab4..7f995491 100644 --- a/cmd/consul-dataplane/main.go +++ b/cmd/consul-dataplane/main.go @@ -32,11 +32,12 @@ var ( logLevel string logJSON bool - nodeName string - nodeID string - serviceID string - namespace string - partition string + nodeName string + nodeID string + serviceID string + serviceIDPath string + namespace string + partition string credentialType string token string @@ -93,6 +94,7 @@ func init() { flag.StringVar(&nodeName, "service-node-name", "", "The name of the Consul node to which the proxy service instance is registered.") flag.StringVar(&nodeID, "service-node-id", "", "The ID of the Consul node to which the proxy service instance is registered.") flag.StringVar(&serviceID, "proxy-service-id", "", "The proxy service instance's ID.") + flag.StringVar(&serviceIDPath, "proxy-service-id-path", "", "The path to a file containing the proxy service instance's ID.") flag.StringVar(&namespace, "service-namespace", "", "The Consul Enterprise namespace in which the proxy service instance is registered.") flag.StringVar(&partition, "service-partition", "", "The Consul Enterprise partition in which the proxy service instance is registered.") @@ -155,6 +157,7 @@ func main() { return } + readServiceIDFromFile() validateFlags() consuldpCfg := &consuldp.Config{ @@ -249,3 +252,19 @@ func main() { log.Fatal(err) } } + +// readServiceIDFromFile reads the service ID from the file specified by the +// -proxy-service-id-path flag. +// +// We do this here, rather than in the consuldp package's config handling, +// because this option only really makes sense as a CLI flag (and we handle +// all flag parsing here). +func readServiceIDFromFile() { + if serviceID == "" && serviceIDPath != "" { + id, err := os.ReadFile(serviceIDPath) + if err != nil { + log.Fatalf("failed to read given -proxy-service-id-path: %v", err) + } + serviceID = string(id) + } +} From 2958bac762f16a016199ca655d7a11df81e8e320 Mon Sep 17 00:00:00 2001 From: Daniel Upton Date: Mon, 17 Oct 2022 14:56:48 +0100 Subject: [PATCH 3/6] docs: document how to add a shell to our container image --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index a7608a06..5f95ba78 100644 --- a/README.md +++ b/README.md @@ -227,3 +227,18 @@ The `consul-dataplane` binary supports the following flags. | `-tls-server-name` | string | | The hostname to expect in the server certificate's subject. This is required if `-addresses` is not a DNS name. | | `-version` | bool | `false` | Prints the current version of consul-dataplane. | | `-xds-bind-addr` | string | `"127.0.0.1"` | The address on which the Envoy xDS server is available. | + +### Extending the Container Image + +The official `hashicorp/consul-dataplane` container image is ["distroless"](https://github.com/GoogleContainerTools/distroless) +and only includes the bare-minimum runtime dependencies, for greater security. + +You may want to add a shell that can be used by the `-addresses exec=...` flag +to resolve Consul servers with a custom script. + +Here's an example of how you might do that, copying `sh` from the busybox image: + +```Dockerfile +FROM hashicorp/consul-dataplane:latest +COPY --from=busybox:uclibc /bin/sh /bin/sh +``` From 7f1c86c7716dd4780bdd0918f0fd67cd7466a6a2 Mon Sep 17 00:00:00 2001 From: Daniel Upton Date: Mon, 17 Oct 2022 15:57:23 +0100 Subject: [PATCH 4/6] Changelog entry --- .changelog/15.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/15.txt diff --git a/.changelog/15.txt b/.changelog/15.txt new file mode 100644 index 00000000..e5769159 --- /dev/null +++ b/.changelog/15.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +Container image is now "distroless" for better security +``` From eb08a05869d23cc4adf6ff0970539fee1e747dfc Mon Sep 17 00:00:00 2001 From: Daniel Upton Date: Fri, 21 Oct 2022 11:31:17 +0100 Subject: [PATCH 5/6] docker: copy Envoy binary from distroless image --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index ef60e0ef..6f8a74fe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,7 @@ # envoy-binary pulls in the latest Envoy binary, as Envoy don't publish # prebuilt binaries in any other form. -FROM envoyproxy/envoy:v1.23.1 as envoy-binary +FROM envoyproxy/envoy-distroless:v1.23.1 as envoy-binary # go-discover builds the discover binary (which we don't currently publish # either). From e0b9ce73994099361dc0b4db5434d36eedf900e8 Mon Sep 17 00:00:00 2001 From: Daniel Upton Date: Fri, 21 Oct 2022 11:31:54 +0100 Subject: [PATCH 6/6] docker: upgrade Envoy to 1.24.0 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 6f8a74fe..78b0ccc0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,7 @@ # envoy-binary pulls in the latest Envoy binary, as Envoy don't publish # prebuilt binaries in any other form. -FROM envoyproxy/envoy-distroless:v1.23.1 as envoy-binary +FROM envoyproxy/envoy-distroless:v1.24.0 as envoy-binary # go-discover builds the discover binary (which we don't currently publish # either).