-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Expand file tree
/
Copy pathDockerfile
More file actions
354 lines (302 loc) · 15.3 KB
/
Copy pathDockerfile
File metadata and controls
354 lines (302 loc) · 15.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
# syntax=docker/dockerfile:1@sha256:ecfaec9ed6d810b56388c508f4121597bfbba70d41a6dfeee4d8cad5f295fc32
# what distro is the image being built for
ARG ALPINE_TAG=3.24.1@sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
ARG DEBIAN_TAG=13.6-slim@sha256:d7e12182ce18b85b93007c1dedf31f2d29e01ccf3182cc4017c709b6259bc132
# renovate: datasource=docker depName=golang versioning=docker
ARG GOLANG_TAG=1.26.6-alpine3.24@sha256:3889b425f035be855a72fb4755265311293b6d414521f0a519d819df32222d83
# renovate: datasource=github-releases depName=hashicorp/terraform versioning=hashicorp
ARG TERRAFORM_1_15_VERSION=1.15.9
# renovate: datasource=github-releases depName=hashicorp/terraform versioning=hashicorp
ARG TERRAFORM_1_16_VERSION=1.16.0
ARG DEFAULT_TERRAFORM_VERSION=${TERRAFORM_1_16_VERSION}
# renovate: datasource=github-releases depName=opentofu/opentofu versioning=hashicorp
ARG DEFAULT_OPENTOFU_VERSION=1.12.6
# renovate: datasource=github-releases depName=open-policy-agent/conftest
ARG DEFAULT_CONFTEST_VERSION=0.66.0
# Stage 1: build artifact and download deps
FROM --platform=$BUILDPLATFORM golang:${GOLANG_TAG} AS builder
# These are automatically populated by Docker
ARG TARGETOS
ARG TARGETARCH
ARG ATLANTIS_VERSION=dev
ENV ATLANTIS_VERSION=${ATLANTIS_VERSION}
ARG ATLANTIS_COMMIT=none
ENV ATLANTIS_COMMIT=${ATLANTIS_COMMIT}
ARG ATLANTIS_DATE=unknown
ENV ATLANTIS_DATE=${ATLANTIS_DATE}
ARG DEFAULT_TERRAFORM_VERSION
ENV DEFAULT_TERRAFORM_VERSION=${DEFAULT_TERRAFORM_VERSION}
ARG DEFAULT_CONFTEST_VERSION
ENV DEFAULT_CONFTEST_VERSION=${DEFAULT_CONFTEST_VERSION}
WORKDIR /app
# This is needed to download transitive dependencies instead of compiling them
# https://github.com/montanaflynn/golang-docker-cache
# https://github.com/golang/go/issues/27719
# renovate: datasource=repology depName=alpine_3_24/bash versioning=loose
ENV BUILDER_BASH_VERSION="5.3.9-r1"
RUN apk add --no-cache \
bash=${BUILDER_BASH_VERSION}
COPY go.mod go.sum ./
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN --mount=type=cache,target=/go/pkg/mod \
go mod graph | awk '{if ($1 !~ "@") print $2}' | xargs go get
COPY . /app
RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -trimpath -ldflags "-s -w -X 'main.version=${ATLANTIS_VERSION}' -X 'main.commit=${ATLANTIS_COMMIT}' -X 'main.date=${ATLANTIS_DATE}'" -v -o atlantis .
FROM debian:${DEBIAN_TAG} AS debian-base
# Define package versions for Debian
# renovate: datasource=repology depName=debian_13/ca-certificates versioning=loose
ENV DEBIAN_CA_CERTIFICATES_VERSION="20250419"
# renovate: datasource=repology depName=debian_13/curl versioning=loose
ENV DEBIAN_CURL_VERSION="8.14.1-2+deb13u4"
# renovate: datasource=repology depName=debian_13/git versioning=loose
ENV DEBIAN_GIT_VERSION="1:2.47.3-0+deb13u1"
# renovate: datasource=repology depName=debian_13/unzip versioning=loose
ENV DEBIAN_UNZIP_VERSION="6.0-29"
# renovate: datasource=repology depName=debian_13/openssh-server versioning=loose
ENV DEBIAN_OPENSSH_SERVER_VERSION="1:10.0p1-7+deb13u4"
# renovate: datasource=repology depName=debian_13/dumb-init versioning=loose
ENV DEBIAN_DUMB_INIT_VERSION="1.2.5-3"
# renovate: datasource=repology depName=debian_13/gnupg versioning=loose
ENV DEBIAN_GNUPG_VERSION="2.4.7-21+deb13u1"
# renovate: datasource=repology depName=debian_13/openssl versioning=loose
ENV DEBIAN_OPENSSL_VERSION="3.5.6-1~deb13u2"
# Set up the 'atlantis' user and adjust permissions. User with uid 1000 is for backwards compatibility
RUN groupadd --gid 1000 atlantis && \
useradd --uid 100 --system --create-home --gid 1000 --shell /bin/bash atlantis && \
useradd --uid 1000 --system --home=/home/atlantis --gid 1000 --shell /bin/bash atlantis2 && \
chown atlantis:atlantis /home/atlantis/ && \
chmod ug+rwx /home/atlantis/
# Install packages needed to run Atlantis.
# We place this last as it will bust less docker layer caches when packages update
RUN apt-get update && \
apt-get install -y --no-install-recommends \
ca-certificates=${DEBIAN_CA_CERTIFICATES_VERSION} \
curl=${DEBIAN_CURL_VERSION} \
git=${DEBIAN_GIT_VERSION} \
unzip=${DEBIAN_UNZIP_VERSION} \
openssh-server=${DEBIAN_OPENSSH_SERVER_VERSION} \
dumb-init=${DEBIAN_DUMB_INIT_VERSION} \
gnupg=${DEBIAN_GNUPG_VERSION} \
openssl=${DEBIAN_OPENSSL_VERSION} && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
FROM debian-base AS deps
# Get the architecture the image is being built for
ARG TARGETPLATFORM
WORKDIR /tmp/build
# install conftest
ARG DEFAULT_CONFTEST_VERSION
ENV DEFAULT_CONFTEST_VERSION=${DEFAULT_CONFTEST_VERSION}
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN AVAILABLE_CONFTEST_VERSIONS=${DEFAULT_CONFTEST_VERSION} && \
case ${TARGETPLATFORM} in \
"linux/amd64") CONFTEST_ARCH=x86_64 ;; \
"linux/arm64") CONFTEST_ARCH=arm64 ;; \
# There is currently no compiled version of conftest for armv7
"linux/arm/v7") CONFTEST_ARCH=x86_64 ;; \
esac && \
for VERSION in ${AVAILABLE_CONFTEST_VERSIONS}; do \
curl -LOs "https://github.com/open-policy-agent/conftest/releases/download/v${VERSION}/conftest_${VERSION}_Linux_${CONFTEST_ARCH}.tar.gz" && \
curl -LOs "https://github.com/open-policy-agent/conftest/releases/download/v${VERSION}/checksums.txt" && \
sed -n "/conftest_${VERSION}_Linux_${CONFTEST_ARCH}.tar.gz/p" checksums.txt | sha256sum -c && \
mkdir -p "/usr/local/bin/cft/versions/${VERSION}" && \
tar -C "/usr/local/bin/cft/versions/${VERSION}" -xzf "conftest_${VERSION}_Linux_${CONFTEST_ARCH}.tar.gz" && \
ln -s "/usr/local/bin/cft/versions/${VERSION}/conftest" /usr/local/bin/conftest && \
rm "conftest_${VERSION}_Linux_${CONFTEST_ARCH}.tar.gz" && \
rm checksums.txt; \
done
# install git-lfs
# renovate: datasource=github-releases depName=git-lfs/git-lfs
ENV GIT_LFS_VERSION=3.8.0
# Keep these hashes in sync with GIT_LFS_VERSION; mismatches fail closed.
# SHA256 hashes are published in the release's signed sha256sums.asc file.
RUN case ${TARGETPLATFORM} in \
"linux/amd64") GIT_LFS_ARCH=amd64; GIT_LFS_SHA256=e455e00f15d9b95661b8d53498ffb0c3367962cf1ec73c31ab7369516cd6ab8d ;; \
"linux/arm64") GIT_LFS_ARCH=arm64; GIT_LFS_SHA256=ac9c8efac980bb0505ead384d087e2acb6486fd8498691a2165fa174ec6118c2 ;; \
"linux/arm/v7") GIT_LFS_ARCH=arm; GIT_LFS_SHA256=67144f93c2342f46456d22dbf33077f54e0581cb91f2966393151871ca328553 ;; \
*) echo "unsupported target platform: ${TARGETPLATFORM}" >&2; exit 1 ;; \
esac && \
curl -L -s --output git-lfs.tar.gz "https://github.com/git-lfs/git-lfs/releases/download/v${GIT_LFS_VERSION}/git-lfs-linux-${GIT_LFS_ARCH}-v${GIT_LFS_VERSION}.tar.gz" && \
echo "${GIT_LFS_SHA256} git-lfs.tar.gz" | sha256sum -c - && \
tar --strip-components=1 -xf git-lfs.tar.gz && \
chmod +x git-lfs && \
mv git-lfs /usr/bin/git-lfs && \
git-lfs --version
# Terraform and OpenTofu live in their own stage so the slim targets never
# download them. The full targets copy from tf-deps; the slim targets do not.
FROM deps AS tf-deps
ARG TARGETPLATFORM
WORKDIR /tmp/build
# install terraform binaries
ARG TERRAFORM_1_15_VERSION
ARG TERRAFORM_1_16_VERSION
ARG DEFAULT_TERRAFORM_VERSION
ENV DEFAULT_TERRAFORM_VERSION=${DEFAULT_TERRAFORM_VERSION}
ARG DEFAULT_OPENTOFU_VERSION
ENV DEFAULT_OPENTOFU_VERSION=${DEFAULT_OPENTOFU_VERSION}
# COPY scripts/download-release.sh .
COPY --from=builder /app/scripts/download-release.sh download-release.sh
# HashiCorp patches only the two most recent minor releases.
RUN ./download-release.sh \
"terraform" \
"${TARGETPLATFORM}" \
"${DEFAULT_TERRAFORM_VERSION}" \
"${TERRAFORM_1_15_VERSION} ${TERRAFORM_1_16_VERSION}" \
&& ./download-release.sh \
"tofu" \
"${TARGETPLATFORM}" \
"${DEFAULT_OPENTOFU_VERSION}" \
"${DEFAULT_OPENTOFU_VERSION}"
# Stage 2 - Alpine
# Creating the individual distro builds using targets.
#
# Each distro has a runtime stage with everything except the Terraform and
# OpenTofu binaries, and two final targets built on it:
# <distro>-slim no Terraform or OpenTofu. Atlantis downloads the version it
# needs at runtime (see --tf-download), so this image carries
# none of the advisories filed against bundled binaries.
# <distro> the runtime stage plus the bundled binaries. This is the
# image that has always been published.
FROM alpine:${ALPINE_TAG} AS alpine-runtime
ARG ATLANTIS_PORT=4141
EXPOSE ${ATLANTIS_PORT}
HEALTHCHECK --interval=5m --timeout=3s \
CMD curl -f http://localhost:${ATLANTIS_PORT:-4141}/healthz || exit 1
# Set up the 'atlantis' user and adjust permissions
RUN addgroup --gid 1000 atlantis && \
adduser -u 100 -S -G atlantis atlantis && \
chown atlantis:root /home/atlantis/ && \
chmod u+rwx /home/atlantis/
# copy atlantis binary
COPY --from=builder /app/atlantis /usr/local/bin/atlantis
# copy dependencies
COPY --from=deps /usr/local/bin/conftest /usr/local/bin/conftest
COPY --from=deps /usr/bin/git-lfs /usr/bin/git-lfs
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
# renovate: datasource=repology depName=alpine_3_24/ca-certificates versioning=loose
ENV CA_CERTIFICATES_VERSION="20260611-r0"
# renovate: datasource=repology depName=alpine_3_24/curl versioning=loose
ENV CURL_VERSION="8.22.0-r0"
# renovate: datasource=repology depName=alpine_3_24/git versioning=loose
ENV GIT_VERSION="2.54.0-r0"
# renovate: datasource=repology depName=alpine_3_24/unzip versioning=loose
ENV UNZIP_VERSION="6.0-r16"
# renovate: datasource=repology depName=alpine_3_24/bash versioning=loose
ENV BASH_VERSION="5.3.9-r1"
# renovate: datasource=repology depName=alpine_3_24/openssh versioning=loose
ENV OPENSSH_VERSION="10.3_p1-r1"
# renovate: datasource=repology depName=alpine_3_24/dumb-init versioning=loose
ENV DUMB_INIT_VERSION="1.2.5-r4"
# renovate: datasource=repology depName=alpine_3_24/gcompat versioning=loose
ENV GCOMPAT_VERSION="1.1.0-r4"
# renovate: datasource=repology depName=alpine_3_24/coreutils versioning=loose
ENV COREUTILS_ENV_VERSION="9.11-r0"
# Install packages needed to run Atlantis.
# We place this last as it will bust less docker layer caches when packages update
RUN apk add --no-cache \
ca-certificates=${CA_CERTIFICATES_VERSION} \
curl=${CURL_VERSION} \
git=${GIT_VERSION} \
unzip=${UNZIP_VERSION} \
bash=${BASH_VERSION} \
openssh=${OPENSSH_VERSION} \
dumb-init=${DUMB_INIT_VERSION} \
gcompat=${GCOMPAT_VERSION} \
coreutils-env=${COREUTILS_ENV_VERSION}
# Strip file capabilities only under fcap_scan_dirs (common rootfs locations for
# binaries and libs: /bin, /sbin, /usr, /opt, /lib, /lib64). This is a scoped
# scan, not a full getcap -r /: walking from / would traverse /proc, /sys, /dev,
# etc. and is slow/noisy. Anything outside fcap_scan_dirs is not checked. Strip
# and verify share the same list; post-pass getcap|grep fails the build if
# capabilities remain under that scope.
# renovate: datasource=repology depName=alpine_3_24/libcap versioning=loose
ENV LIBCAP_VERSION="2.78-r0"
# hadolint ignore=DL4006
RUN fcap_scan_dirs="/bin /sbin /usr /opt /lib /lib64" && \
apk add --no-cache libcap=${LIBCAP_VERSION} && \
command -v getcap >/dev/null && command -v setcap >/dev/null && \
for d in $fcap_scan_dirs; do \
[ -d "$d" ] && getcap -r "$d" 2>/dev/null; \
done | awk '{ print $1 }' | sort -u | while read -r f; do \
[ -n "$f" ] && { setcap -r "$f" 2>/dev/null || echo "warning: could not strip caps from $f" >&2; }; \
done && \
remaining="$(for d in $fcap_scan_dirs; do [ -d "$d" ] && getcap -r "$d" 2>/dev/null || :; done)" && \
if [ -n "$remaining" ]; then \
echo "failed to remove all file capabilities (post-pass getcap under fcap_scan_dirs):" >&2; \
echo "$remaining" >&2; \
exit 1; \
fi && \
apk del libcap
ARG DEFAULT_CONFTEST_VERSION
ENV DEFAULT_CONFTEST_VERSION=${DEFAULT_CONFTEST_VERSION}
# Set the entry point to the atlantis user and run the atlantis command
USER atlantis
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["server"]
FROM alpine-runtime AS alpine-slim
# Nothing to add. With no terraform on PATH, Atlantis refuses to start until
# --default-tf-version is set (flag, ATLANTIS_DEFAULT_TF_VERSION, or the
# server config file) and then downloads that version on first use.
#
# Deliberately no ENV ATLANTIS_DEFAULT_TF_VERSION here: environment variables
# take precedence over the server config file, so a version baked into the
# image would silently override a version pinned in that file.
FROM alpine-runtime AS alpine
# copy terraform binaries. These come out of release zip files, which carry no
# file capabilities, so the capability strip in alpine-runtime still holds.
COPY --from=tf-deps /usr/local/bin/terraform/terraform* /usr/local/bin/
COPY --from=tf-deps /usr/local/bin/tofu/tofu* /usr/local/bin/
# Stage 2 - Debian
FROM debian-base AS debian-runtime
ARG ATLANTIS_PORT=4141
EXPOSE ${ATLANTIS_PORT}
HEALTHCHECK --interval=5m --timeout=3s \
CMD curl -f http://localhost:${ATLANTIS_PORT:-4141}/healthz || exit 1
# copy atlantis binary
COPY --from=builder /app/atlantis /usr/local/bin/atlantis
# copy dependencies
COPY --from=deps /usr/local/bin/conftest /usr/local/bin/conftest
COPY --from=deps /usr/bin/git-lfs /usr/bin/git-lfs
# copy docker-entrypoint.sh
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
ARG DEFAULT_CONFTEST_VERSION
ENV DEFAULT_CONFTEST_VERSION=${DEFAULT_CONFTEST_VERSION}
# Same as Alpine: strip and verify only under fcap_scan_dirs (scoped rootfs
# trees, not the entire image). Post-pass: if getcap still reports any
# "path = cap_set" line under that scope, the build fails. Strip may use
# 2>/dev/null and setcap || true; verification is the hard guarantee.
# renovate: datasource=repology depName=debian_13/libcap2-bin versioning=loose
ENV DEBIAN_LIBCAP2_BIN_VERSION="1:2.75-10+deb13u1+b1"
# hadolint ignore=DL4006
RUN fcap_scan_dirs="/bin /sbin /usr /opt /lib /lib64" && \
apt-get update && \
apt-get install -y --no-install-recommends libcap2-bin=${DEBIAN_LIBCAP2_BIN_VERSION} && \
command -v getcap >/dev/null && command -v setcap >/dev/null && \
for d in $fcap_scan_dirs; do \
[ -d "$d" ] && getcap -r "$d" 2>/dev/null; \
done | awk '{ print $1 }' | sort -u | while read -r f; do \
[ -n "$f" ] && { setcap -r "$f" 2>/dev/null || echo "warning: could not strip caps from $f" >&2; }; \
done && \
remaining="$(for d in $fcap_scan_dirs; do [ -d "$d" ] && getcap -r "$d" 2>/dev/null || :; done)" && \
if [ -n "$remaining" ]; then \
echo "failed to remove all file capabilities (post-pass getcap under fcap_scan_dirs):" >&2; \
echo "$remaining" >&2; \
exit 1; \
fi && \
apt-get purge -y libcap2-bin && \
apt-get autoremove -y && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# Set the entry point to the atlantis user and run the atlantis command
USER atlantis
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["server"]
FROM debian-runtime AS debian-slim
# Nothing to add. See alpine-slim for why no default Terraform version is set.
FROM debian-runtime AS debian
# copy terraform binaries. See the alpine target for why this is safe to do
# after the capability strip.
COPY --from=tf-deps /usr/local/bin/terraform/terraform* /usr/local/bin/
COPY --from=tf-deps /usr/local/bin/tofu/tofu* /usr/local/bin/