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

build protoc-* containers as nonroot #473

Merged
merged 2 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
44 changes: 24 additions & 20 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ PROTOC_TYPESCRIPT_IMAGE = protoc-typescript
RUST_ACTION ?= run -p sigstore-protobuf-specs-codegen

PLATFORM ?= linux/amd64
UID ?= $(shell id -u)
GID ?= $(shell id -g)
DOCKER_BUILD = docker build --platform ${PLATFORM} --build-arg UID=${UID} --build-arg GID=${GID}
bobcallaway marked this conversation as resolved.
Show resolved Hide resolved
DOCKER_RUN = docker run --platform ${PLATFORM} --user ${UID}:${GID}

PROTOS = $(shell find protos/ -iname "*.proto" | sed 's|^|/defs/|')

Expand All @@ -33,71 +37,71 @@ all: go python typescript ruby jsonschema rust
# generate Go protobuf code
go: docker-image
@echo "Generating go proto Docker image"
cd protoc-builder && docker build --platform ${PLATFORM} -t ${PROTOC_GO_IMAGE} -f Dockerfile.go .
cd protoc-builder && ${DOCKER_BUILD} -t ${PROTOC_GO_IMAGE} -f Dockerfile.go .
@echo "Generating go protobuf files"
docker run --platform ${PLATFORM} -v ${PWD}:/defs ${PROTOC_GO_IMAGE} \
${DOCKER_RUN} -v ${PWD}:/defs ${PROTOC_GO_IMAGE} \
-I/opt/include -I/googleapis -I/defs/protos \
--go_opt=module=github.com/sigstore/protobuf-specs/gen/pb-go --go_out=/defs/gen/pb-go ${PROTOS}

python: docker-image
@echo "Generating python proto Docker image"
cd protoc-builder && docker build --platform ${PLATFORM} -t ${PROTOC_PYTHON_IMAGE} -f Dockerfile.python .
cd protoc-builder && ${DOCKER_BUILD} -t ${PROTOC_PYTHON_IMAGE} -f Dockerfile.python .
@echo "Generating python protobuf files"
docker run --platform ${PLATFORM} -v ${PWD}:/defs ${PROTOC_PYTHON_IMAGE} \
${DOCKER_RUN} -v ${PWD}:/defs ${PROTOC_PYTHON_IMAGE} \
-I/opt/include -I/googleapis -I/defs/protos \
--python_betterproto_opt=pydantic_dataclasses --python_betterproto_out=/defs/gen/pb-python/sigstore_protobuf_specs ${PROTOS}

typescript: docker-image
@echo "Generating typescript proto Docker image"
cd protoc-builder && docker build --platform ${PLATFORM} -t ${PROTOC_TYPESCRIPT_IMAGE} -f Dockerfile.typescript .
cd protoc-builder && ${DOCKER_BUILD} -t ${PROTOC_TYPESCRIPT_IMAGE} -f Dockerfile.typescript .
@echo "Generating javascript protobuf files"
docker run --platform ${PLATFORM} -v ${PWD}:/defs ${PROTOC_TYPESCRIPT_IMAGE} \
${DOCKER_RUN} -v ${PWD}:/defs ${PROTOC_TYPESCRIPT_IMAGE} \
-I/opt/include -I/googleapis -I/defs/protos \
--ts_proto_out=/defs/gen/pb-typescript/src/__generated__ --ts_proto_opt=oneof=unions,forceLong=string,env=node,exportCommonSymbols=false,outputPartialMethods=false,outputEncodeMethods=false,unrecognizedEnum=false ${PROTOS}

ruby: docker-image
@echo "Generating ruby proto Docker image"
cd protoc-builder && docker build --platform ${PLATFORM} -t ${PROTOC_RUBY_IMAGE} -f Dockerfile.ruby .
cd protoc-builder && ${DOCKER_BUILD} -t ${PROTOC_RUBY_IMAGE} -f Dockerfile.ruby .
@echo "Generating ruby protobuf files"
docker run --platform ${PLATFORM} -v ${PWD}:/defs ${PROTOC_RUBY_IMAGE} \
${DOCKER_RUN} -v ${PWD}:/defs ${PROTOC_RUBY_IMAGE} \
-I/opt/include -I/googleapis -I/defs/protos --ruby_out=/defs/gen/pb-ruby/lib ${PROTOS}

jsonschema: docker-image
@echo "Generating jsonschema proto Docker image"
cd protoc-builder && docker build --platform ${PLATFORM} -t ${PROTOC_JSONSCHEMA_IMAGE} -f Dockerfile.jsonschema .
cd protoc-builder && ${DOCKER_BUILD} -t ${PROTOC_JSONSCHEMA_IMAGE} -f Dockerfile.jsonschema .
@echo "Generating JSON schema files"
mkdir -p gen/jsonschema/schemas
docker run --platform ${PLATFORM} -v ${PWD}:/defs ${PROTOC_JSONSCHEMA_IMAGE} \
${DOCKER_RUN} -v ${PWD}:/defs ${PROTOC_JSONSCHEMA_IMAGE} \
-I/opt/include -I/googleapis -I/defs/protos \
--jsonschema_out=/defs/gen/jsonschema/schemas --jsonschema_opt=disallow_additional_properties --jsonschema_opt=enforce_oneof --jsonschema_opt=enums_as_strings_only --jsonschema_opt=file_extension=schema.json --jsonschema_opt=json_fieldnames \
${PROTOS}

rust: docker-image
@echo "Generating rust proto Docker image"
cd protoc-builder && docker build --platform ${PLATFORM} -t ${PROTOC_RUST_IMAGE} -f Dockerfile.rust .
docker run --platform ${PLATFORM} -v ${PWD}:/defs \
cd protoc-builder && ${DOCKER_BUILD} -t ${PROTOC_RUST_IMAGE} -f Dockerfile.rust .
${DOCKER_RUN} -v ${PWD}:/defs \
-e "RUST_BACKTRACE=1" -e "CARGO_REGISTRY_TOKEN" ${PROTOC_RUST_IMAGE} \
-c "cd /defs/gen/pb-rust && cargo ${RUST_ACTION}"

# docker already does its own caching so we can attempt a build every time
.PHONY: docker-image
docker-image:
@echo "Building base docker image"
cd protoc-builder && docker build --platform ${PLATFORM} -t ${PROTOC_IMAGE} -f Dockerfile.protoc .
cd protoc-builder && ${DOCKER_BUILD} -t ${PROTOC_IMAGE} -f Dockerfile.protoc .

# to recover from a situation where a stale layer exist, just purging the
# docker image via `make clean` is not enough. Re-building without layer
# cache is the only solution.
.PHONY: docker-image-no-cache
docker-image-no-cache:
@echo "Building development docker images with disabled cache"
cd protoc-builder && docker build --no-cache --platform ${PLATFORM} -t ${PROTOC_IMAGE} -f Dockerfile.protoc .
cd protoc-builder && docker build --no-cache --platform ${PLATFORM} -t ${PROTOC_GO_IMAGE} -f Dockerfile.go .
cd protoc-builder && docker build --no-cache --platform ${PLATFORM} -t ${PROTOC_JSONSCHEMA_IMAGE} -f Dockerfile.jsonschema .
cd protoc-builder && docker build --no-cache --platform ${PLATFORM} -t ${PROTOC_PYTHON_IMAGE} -f Dockerfile.python .
cd protoc-builder && docker build --no-cache --platform ${PLATFORM} -t ${PROTOC_RUBY_IMAGE} -f Dockerfile.ruby .
cd protoc-builder && docker build --no-cache --platform ${PLATFORM} -t ${PROTOC_RUST_IMAGE} -f Dockerfile.rust .
cd protoc-builder && docker build --no-cache --platform ${PLATFORM} -t ${PROTOC_TYPESCRIPT_IMAGE} -f Dockerfile.typescript .
cd protoc-builder && ${DOCKER_BUILD} -t ${PROTOC_IMAGE} -f Dockerfile.protoc .
cd protoc-builder && ${DOCKER_BUILD} -t ${PROTOC_GO_IMAGE} -f Dockerfile.go .
cd protoc-builder && ${DOCKER_BUILD} -t ${PROTOC_JSONSCHEMA_IMAGE} -f Dockerfile.jsonschema .
cd protoc-builder && ${DOCKER_BUILD} -t ${PROTOC_PYTHON_IMAGE} -f Dockerfile.python .
cd protoc-builder && ${DOCKER_BUILD} -t ${PROTOC_RUBY_IMAGE} -f Dockerfile.ruby .
cd protoc-builder && ${DOCKER_BUILD} -t ${PROTOC_RUST_IMAGE} -f Dockerfile.rust .
cd protoc-builder && ${DOCKER_BUILD} -t ${PROTOC_TYPESCRIPT_IMAGE} -f Dockerfile.typescript .

# clean up generated files (not working? try sudo make clean)
clean:
Expand Down
2 changes: 1 addition & 1 deletion protoc-builder/Dockerfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ ADD hack/go/go.* hack/go/tools.go tools/
RUN cd tools && go build --trimpath google.golang.org/grpc/cmd/protoc-gen-go-grpc
RUN cd tools && go build --trimpath google.golang.org/protobuf/cmd/protoc-gen-go

FROM gcr.io/distroless/cc-debian12@sha256:b7550f0b15838de14c564337eef2b804ba593ae55d81ca855421bd52f19bb480
FROM gcr.io/distroless/cc-debian12:nonroot@sha256:6970a2b2cb07c68f3e15d1b5d2ba857e53da911d5d321f48a842d6b0d26984cf

COPY --from=go-builder /go/tools/protoc-* /usr/local/bin/
COPY --from=protoc-base /protobuf/bin/protoc /usr/local/bin/
Expand Down
19 changes: 16 additions & 3 deletions protoc-builder/Dockerfile.protoc
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,36 @@
# This container grabs the protoc compiler and the googleapi includes
# /protobuf will contain the extracted protoc
# /googleapis will contain the various googleapis proto imports one might need
FROM alpine:3.21@sha256:56fa17d2a7e7f168a043a2712e63aed1f8543aeafdcee47c58dcffe38ed51099 AS protoc-builder
FROM debian:bullseye-slim@sha256:6344a6747740d465bff88e833e43ef881a8c4dd51950dba5b30664c93f74cbef AS protoc-builder

# Create output directories
RUN mkdir /protobuf /googleapis
# Install needed utilities
RUN apt-get update && apt-get install -y unzip git

# Set up user and group to match host we're building the container on
ARG UID
ARG GID
bobcallaway marked this conversation as resolved.
Show resolved Hide resolved

RUN addgroup --gid ${GID} myuser && adduser --uid ${UID} --ingroup myuser --disabled-password myuser
bobcallaway marked this conversation as resolved.
Show resolved Hide resolved

# Set permissions on the output directories so the user can write to them
RUN chown myuser:myuser /protobuf /googleapis
bobcallaway marked this conversation as resolved.
Show resolved Hide resolved

# Switch to user to execute the remaining commands
USER myuser

# Download specific release of protoc
# TODO: add dependabot-like feature to check for release updates
ARG PROTOC_VERSION=v21.6
ARG PROTOC_CHECKSUM=sha256:6a9fc36363a2d05d73fc363a46cd57d849068d33305db39f77daac8ba073e818

ADD --checksum=${PROTOC_CHECKSUM} https://github.com/protocolbuffers/protobuf/releases/download/${PROTOC_VERSION}/protoc-${PROTOC_VERSION#v}-linux-x86_64.zip /tmp/protoc.zip
ADD --chown=myuser:myuser --checksum=${PROTOC_CHECKSUM} https://github.com/protocolbuffers/protobuf/releases/download/${PROTOC_VERSION}/protoc-${PROTOC_VERSION#v}-linux-x86_64.zip /tmp/protoc.zip
bobcallaway marked this conversation as resolved.
Show resolved Hide resolved
RUN unzip -d /protobuf /tmp/protoc.zip
RUN chmod 755 /protobuf/bin/protoc

# fetch specific commit of googleapis
# TODO: add dependabot-like feature to check for release updates
RUN apk add git
ARG GOOGLE_APIS_VERSION=6ef9eaea379fc1cc0355e06a5a20b594543ee693
#ARG GOOGLE_APIS_VERSION=95f0f2b2aee51e460646320d6e8f2ce75c463f5a
RUN git clone --filter=tree:0 https://github.com/googleapis/googleapis.git /googleapis && \
Expand Down
2 changes: 1 addition & 1 deletion protoc-builder/Dockerfile.ruby
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM gcr.io/distroless/cc-debian12@sha256:b7550f0b15838de14c564337eef2b804ba593ae55d81ca855421bd52f19bb480
FROM gcr.io/distroless/cc-debian12:nonroot@sha256:6970a2b2cb07c68f3e15d1b5d2ba857e53da911d5d321f48a842d6b0d26984cf

COPY --from=protoc-base /protobuf/bin/protoc /usr/local/bin/
COPY --from=protoc-base /protobuf/include/google /opt/include/google
Expand Down
4 changes: 2 additions & 2 deletions protoc-builder/Dockerfile.typescript
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ WORKDIR /app
RUN npm ci --install-strategy=shallow

# /usr/bin/env is called from ts-proto but not in distroless by default; we use busybox for this
FROM gcr.io/distroless/base-debian12:debug@sha256:a6b40812524ed4f6a2e507eb01ed04f867d9b4cb1e20369d62ffef0edd598efd AS env-source
FROM gcr.io/distroless/base-debian12:debug-nonroot@sha256:ee694eefd7685d8c443fec6abd3bff8e30c437faa8fbeacc0ce4c2e847d45501 AS env-source

FROM gcr.io/distroless/nodejs18-debian12@sha256:06ded55200d9a39c71e85117775d1ac63bfcb20104e53e6c6de24832ed2cf015
FROM gcr.io/distroless/nodejs18-debian12:nonroot@sha256:4a5e24618b1cb8743b7651cb7d91de58a7e71f470e72b879c49e59115f2f5a45

# node is installed in a non-default location in distroless
ENV PATH=$PATH:/nodejs/bin
Expand Down
Loading