Skip to content
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

Make our container image "distroless" #15

Merged
merged 6 commits into from
Nov 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/15.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
Container image is now "distroless" for better security
```
42 changes: 15 additions & 27 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,27 @@

# 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.24.0 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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to use the (smaller) static-debian11 image, but unfortunately, Envoy dynamically links system libraries so this is not possible.


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

Expand All @@ -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
Expand All @@ -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 && \
Expand All @@ -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
Expand Down
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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. |
Expand All @@ -226,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
```
29 changes: 24 additions & 5 deletions cmd/consul-dataplane/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.")

Expand Down Expand Up @@ -155,6 +157,7 @@ func main() {
return
}

readServiceIDFromFile()
validateFlags()

consuldpCfg := &consuldp.Config{
Expand Down Expand Up @@ -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).
Comment on lines +260 to +261
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it'd make sense to add this to the config file, what do you think?

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)
}
}
Comment on lines +256 to +270
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @ishustava @thisisnotashwin 👋🏻

Do you have an explanation of why consul-k8s needs this, rather than putting the service ID directly in the CLI flags, that I could put here?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In K8s, we often have a different container responsible for getting the service ID to begin with. Information across containers can only be shared via files on a shared volume. It makes it significantly easier to ready what is going on if we are dealing with filepath. Putting the service ID directly to the CLI would require reading the contents of that file anyway.