L2 Base Node Builds are Insecure
The docker builds for L2 base/node are full of holes
Ethereum is based on zero trust and mathematically sound security. But the Base L2 Node (https://github.com/base/node/tree/main/geth) isn’t being built securely. So, what is wrong with the existing Base docker image?
A Caveat
I’m in no way an Ethereum expert, nor have I ever run an Ethereum node before. I am just a regular guy who wants to run a node for fun and solve my itch.
Then Why Rebuild?
Because I am paranoid and security obsessed and want fewer moving parts in the build and runtime. Especially when it involves (to the moon baby!).
The Problem
Remember the 2021 CodeCov Bash Uploader compromise? A modified script exfiltrated secrets from thousands of build environments. The original geth image in base/node uses the same kind of risky pattern: RUN curl -sSfL ‘<https://just.systems/install.sh>’ | bash -s -- --to /usr/local/bin. This pipes an untrusted script directly to bash without checksum verification. This is a classic supply chain attack.
So what is wrong with the existing image for geth?
It has a shell, package manager, and unnecessary binaries.
It is not a static binary build.
It does not use pinned base images with SHA256 digests. Without the images being pinned, they could have been tampered with and you wouldn’t have a clue what you’re building.
It is a root user. A root use can do anything.
It exposes all ports, not just the necessary ones. Its like leaving your house with all the doors and windows open.
It isn’t a reproducible build.
The Solution
How did I secure the build for geth?
Pinned base images with SHA256 digests. This ensures the image can’t be tampered with and the build is reproducible.
Static Binary Build.
CGO_ENABLED=0So, no libc dependencies and no dynamic linking.Being paranoid I am explicitly setting the user to nonroot even though I know the distroless is already nonroot.
Expose only the necessary ports. I am used to clicking the lock button twice on my car to ensure the doors are locked even though they were already locked on the first press of the button. I don’t want all the ports open on my docker image.
The final image is a single binary, there are no other tools or dependencies in the image. Less patching means fewer vulnerabilities and fewer dreaded Dependabot PRs.
If there is a zero day in the binary, it will be harder to exploit since we don’t have these additional tools. Less code means fewer vulnerabilities (the famous nocode
philosophy: no code = no bugs).
The Build
FROM golang:1.23@sha256:60deed95d3888cc5e4d9ff8a10c54e5edc008c6ae3fba6187be6fb592e19e8c0 AS build
WORKDIR /app
# Build arguments for version info
ARG OP_NODE_REPO=https://github.com/ethereum-optimism/optimism.git
ARG OP_NODE_TAG=op-node/v1.14.1
ARG OP_NODE_COMMIT=c1081e3ad0004590435e3179e583cdfdbdd6bc61
RUN git clone $OP_NODE_REPO --branch $OP_NODE_TAG --single-branch . && \\
git switch -c branch-$OP_NODE_TAG && \\
[ “$(git rev-parse HEAD)” = “$OP_NODE_COMMIT” ]
RUN cd op-node && \\
GITCOMMIT=$(git rev-parse HEAD) && \\
GITDATE=$(git show -s --format=%cI HEAD) && \\
CGO_ENABLED=0 GOOS=linux go build -v \\
-ldflags “-s -w -X main.GitCommit=${GITCOMMIT} -X main.GitDate=${GITDATE} -X github.com/ethereum-optimism/optimism/op-node/version.Version=${OP_NODE_TAG} -X github.com/ethereum-optimism/optimism/op-node/version.Meta=stable” \\
-o /bin/op-node \\
./cmd
FROM gcr.io/distroless/static:nonroot@sha256:e8a4044e0b4ae4257efa45fc026c0bc30ad320d43bd4c1a7d5271bd241e386d0
WORKDIR /app
COPY --from=build /bin/op-node ./
USER nonroot
EXPOSE 9545 9222 9222/udp 7300
ENTRYPOINT [”./op-node”]
What This Doesn’t Solve
So is geth secure? Kind of.
How do you trust the build environment? Does your build environment have any vulnerabilities? https://substack.bomfather.dev/p/githubs-ubuntu-latest-runners-have ;)
What happens to runtime security like access to the secrets and keys? Do you trust the runtime environment?
Crypto was built to have zero trust, but it has to run somewhere, so a new problem might be centered around whether your k8s secure? How about your cloud provider, are they secure? Or your hardware, do you know if it is secure?
What about the insider threat?
All of this is for another day.
Was It Worth It?
Is this overkill for a testnet node? Yes.
Would I do it again? Also yes.
Will this stop nation-state actors? LMAO no.
Will it stop some script kiddie running automated scans? Maybe!
For a production mainnet node handling real funds? Absolutely.
My next project: convincing myself that my hardware isn’t already compromised from the factory. (Spoiler: it probably is, but ignorance is bliss.)
To the moon! 🚀 (But securely.)

